Skip to main content
Code Review

Return to Question

added 69 characters in body; edited tags; edited title
Source Link
200_success
  • 145.5k
  • 22
  • 190
  • 479

React: do I use state app with table, search & props in a proper way?pagination

HereI'm new to React. I major in Angular. And for me it's realy not so obvious here... Do I use this props & state variables in a good way or do I need to refactor/change something?

Main goal is my codesuch: simple app with table, search & pagination. URL-state with query params should lead to a proper page, sorting etc when somebody copy-paste the url. All params in URL are optional (so I do not like the idea of using url like /:offset/:limit/:search etc).

My repository: https://bitbucket.org/kot_matpockuh/react_test_app/src/

Maybe I have any other weird code?

I'm new to React. I major in Angular. And for me it's realy not so obvious here... Do I use this props & state variables in a good way or do I need to refactor/change something?

Main goal is such: simple app with table, search & pagination. URL-state with query params should lead to a proper page, sorting etc when somebody copy-paste the url. All params in URL are optional (so I do not like the idea of using url like /:offset/:limit/:search etc).

My repository:

https://bitbucket.org/kot_matpockuh/react_test_app/src/master/

Maybe I have any other weird code?

React: do I use state & props in a proper way?

Here is my code:

I'm new to React. I major in Angular. And for me it's realy not so obvious here... Do I use this props & state variables in a good way or do I need to refactor/change something?

Main goal is such: simple app with table, search & pagination. URL-state with query params should lead to a proper page, sorting etc when somebody copy-paste the url. All params in URL are optional (so I do not like the idea of using url like /:offset/:limit/:search etc).

My repository:

https://bitbucket.org/kot_matpockuh/react_test_app/src/master/

Maybe I have any other weird code?

React app with table, search & pagination

I'm new to React. I major in Angular. And for me it's realy not so obvious here... Do I use this props & state variables in a good way or do I need to refactor/change something?

Main goal is such: simple app with table, search & pagination. URL-state with query params should lead to a proper page, sorting etc when somebody copy-paste the url. All params in URL are optional (so I do not like the idea of using url like /:offset/:limit/:search etc).

My repository: https://bitbucket.org/kot_matpockuh/react_test_app/src/

Maybe I have any other weird code?

Tweeted twitter.com/StackCodeReview/status/1006495467360915456
deleted 5 characters in body
Source Link
Mast
  • 13.8k
  • 12
  • 57
  • 127

Also maybeMaybe I have any other wierd-codeweird code?

Also maybe I have any other wierd-code?

Maybe I have any other weird code?

Source Link
pavel
  • 141
  • 3

React: do I use state & props in a proper way?

Here is my code:

tractorsList.js

import React, {Component} from 'react';
import TractorService from '../../services/tractor';
import PaginationBar from '../../shared/pagination/pagination';
import TableHeader from '../../shared/tableHeader/tableHeader';
import {GlobalConst} from '../../constants/global';
import * as qs from 'query-string';
class TractorList extends Component {
 // define state variables
 state = {
 currentPage: 0,
 pageParams: {
 offset: 0,
 limit: GlobalConst.defaultPageLim,
 sortField: undefined,
 sortOrder: undefined,
 search: undefined
 },
 tractors: {
 total: 0,
 data: []
 }
 };
 //define class variables
 isLoading = false;
 componentWillMount() {
 //parse URL-params (when somebody puts a link with params into browser)
 const parsedUrl = qs.parse(window.location.search);
 const objKeys = Object.keys(parsedUrl);
 const tempStateData = this.state.pageParams;
 for (const keyItem of objKeys) {
 if (this.state.pageParams.hasOwnProperty(keyItem)) {
 if (keyItem === 'offset' || keyItem === 'limit') {
 tempStateData[keyItem] = Number(parsedUrl[keyItem] || 0);
 } else {
 tempStateData[keyItem] = parsedUrl[keyItem];
 }
 }
 }
 this.setState({
 pageParams: tempStateData
 }, () => {
 // to be sure: state have updated already & update current pagination page
 if (this.state.pageParams.offset > 0) {
 this.setState({
 currentPage: Math.ceil(this.state.pageParams.offset / this.state.pageParams.limit)
 });
 }
 });
 }
 componentDidMount() {
 // after all URL & state manipulations: get data from the server
 this.getTractors();
 }
 getTractors() {
 // here we use service
 this.clearTractorsList();
 this.isLoading = true;
 const data = this.state.pageParams;
 TractorService.getTractors(data)
 .finally(() => {
 this.isLoading = false;
 })
 .then(res => {
 this.setState({tractors: res.data});
 })
 }
 clearTractorsList() {
 // first of all clear existing list
 this.setState({
 tractors: {
 total: 0,
 data: []
 }
 });
 }
 changePage(pageNr, limit) {
 // get data from pagination component and transform it & change URL
 const offset = (pageNr) * limit > 0 ? (pageNr * limit) : 0;
 const statePageParams = this.state.pageParams;
 statePageParams.offset = offset;
 statePageParams.limit = limit;
 this.setState({
 currentPage: pageNr,
 pageParams: statePageParams
 });
 this.props.history.push({
 pathname: '/tractors',
 search: qs.stringify(this.state.pageParams)
 });
 this.getTractors();
 }
 setSorting(sortField, sortOrder) {
 // get data from table-header component and transform it & change URL
 const statePageParams = this.state.pageParams;
 statePageParams.offset = 0;
 statePageParams.sortField = sortField;
 statePageParams.sortOrder = sortOrder;
 this.setState({
 currentPage: 0,
 pageParams: statePageParams
 });
 this.props.history.push({
 pathname: '/tractors',
 search: qs.stringify(this.state.pageParams)
 });
 this.getTractors();
 }
 setSearchValue(val){
 // get data from search input, transform it
 const statePageParams = this.state.pageParams;
 statePageParams.offset = 0;
 statePageParams.search = val;
 this.setState({
 currentPage: 0,
 pageParams: statePageParams
 });
 }
 searchTractors(event) {
 // change URL & get data
 event.preventDefault();
 this.props.history.push({
 pathname: '/tractors',
 search: qs.stringify(this.state.pageParams)
 });
 this.getTractors();
 }
 checkIfFieldIsSorted(field) {
 // check: was field sorted or not?
 return this.state.pageParams.sortField === field;
 }
 checkWayOfSorting() {
 // check: was field sorted asc/desc?
 return this.state.pageParams.sortOrder === 'asc' ? true : false;
 }
 render() {
 return (
 <div>
 <form onSubmit={event => this.searchTractors(event)}>
 <div className="input-group mb-3">
 <input type="text" className="form-control" placeholder="Find..."
 onChange={e => this.setSearchValue(e.target.value)}/>
 <div className="input-group-append">
 <button className="btn btn-outline-secondary" type="submit">⌕</button>
 </div>
 </div>
 </form>
 <div className="grid-table">
 <div className="grid-table-header">
 <div className="grid-table-header-col">
 <TableHeader text={"Reg. Number"} field={"registrationNumber"}
 isActive={this.checkIfFieldIsSorted("registrationNumber")}
 sortOrder={this.checkWayOfSorting()}
 onSortingChanged={this.setSorting.bind(this)}></TableHeader>
 </div>
 <div className="grid-table-header-col">
 <TableHeader text={"Manufacturer"} field={"manufacturer"}
 isActive={this.checkIfFieldIsSorted("manufacturer")}
 sortOrder={this.checkWayOfSorting()}
 onSortingChanged={this.setSorting.bind(this)}></TableHeader>
 </div>
 <div className="grid-table-header-col">
 <TableHeader text={"Model"} field={"model"}
 isActive={this.checkIfFieldIsSorted("model")}
 sortOrder={this.checkWayOfSorting()}
 onSortingChanged={this.setSorting.bind(this)}></TableHeader>
 </div>
 <div className="grid-table-header-col">
 <TableHeader text={"Actual Mileage"} field={"actualMileage"}
 isActive={this.checkIfFieldIsSorted("actualMileage")}
 sortOrder={this.checkWayOfSorting()}
 onSortingChanged={this.setSorting.bind(this)}></TableHeader>
 </div>
 </div>
 <div className="grid-table-body">
 {
 this.state.tractors.data.map(tractor =>
 <div className="grid-table-body-row" key={tractor._id}>
 <div className="grid-table-body-row-col">
 {tractor.registrationNumber}
 </div>
 <div className="grid-table-body-row-col">
 {tractor.manufacturer}
 </div>
 <div className="grid-table-body-row-col">
 {tractor.model}
 </div>
 <div className="grid-table-body-row-col">
 {tractor.actualMileage}
 </div>
 </div>)
 }
 </div>
 </div>
 <PaginationBar total={this.state.tractors.total} current={this.state.currentPage}
 pageSize={Number(this.state.pageParams.limit)}
 onShowSizeChange={this.changePage.bind(this)}>
 </PaginationBar>
 {
 this.isLoading && <span>Loading....</span>
 }
 </div>
 )
 }
}
export default TractorList;

pagination.js

import React, {Component} from 'react';
import {GlobalConst} from '../../constants/global';
import PropTypes from 'prop-types';
import ReactPaginate from 'react-paginate';
import './pagination.css';
class PaginationBar extends Component {
 static propTypes = {
 current: PropTypes.number,
 total: PropTypes.number,
 pageSize: PropTypes.number,
 onShowSizeChange: PropTypes.func
 };
 static defaultProps = {
 current: GlobalConst.defaultPageNr,
 total: 0,
 pageSize: GlobalConst.defaultPageLim
 };
 perPageOptions = [10, 25, 50, 100];
 constructor(props) {
 super(props);
 this.state = {
 pageSize: null
 }
 }
 componentDidMount() {
 // copy props to state, so we can use it (state) in this component
 const pageSize = this.props.pageSize;
 this.setState({
 pageSize: pageSize
 })
 }
 onShowSizeChange = (data) => {
 // when we change perPage drop-down
 this.setState({pageSize: data.target.value});
 this.props.onShowSizeChange(0, data.target.value);
 }
 onChange = (data) => {
 // when we change current page (pagination)
 this.props.onShowSizeChange(data.selected, this.props.pageSize);
 }
 render() {
 return (
 <nav className="d-flex justify-content-center position-relative pagination">
 <div className="pagination-per-page">
 <div className="form-group row align-items-center">
 <label htmlFor="perPageSelect" className="col-12 col-lg-6 pr-0 m-0">Per page:</label>
 <div className="col-12 col-lg-6">
 <select className="form-control" id="perPageSelect"
 onChange={this.onShowSizeChange}
 value={this.props.pageSize}>
 {
 this.perPageOptions.map((option, index) =>
 <option key={index}>{option}</option>)
 }
 </select>
 </div>
 </div>
 </div>
 <ReactPaginate previousLabel={"«"}
 nextLabel={"»"}
 breakLabel={<a href="" className="page-link">...</a>}
 breakClassName={"page-item disabled"}
 initialPage={this.props.current}
 forcePage={this.props.current}
 pageCount={Math.ceil(this.props.total / this.state.pageSize)}
 marginPagesDisplayed={2}
 pageRangeDisplayed={4}
 onPageChange={this.onChange}
 disableInitialCallback={true}
 containerClassName={"pagination"}
 activeClassName={"active"}
 pageClassName={"page-item"}
 pageLinkClassName={"page-link"}
 previousClassName={"page-item"}
 nextClassName={"page-item"}
 previousLinkClassName={"page-link"}
 nextLinkClassName={"page-link"}/>
 </nav>
 )
 }
}
export default PaginationBar;

tableHeader.js

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import './tableHeader.css';
class TableHeader extends Component {
 state = {
 sortOrder: true,
 isActive: false
 };
 static propTypes = {
 text: PropTypes.string,
 field: PropTypes.string,
 isActive: PropTypes.bool,
 sortOrder: PropTypes.bool,
 onSortingChanged: PropTypes.func
 };
 static defaultProps = {
 text: '',
 sortOrder: undefined
 };
 componentDidMount() {
 // init our data
 this.setState({
 isActive: this.props.isActive,
 sortOrder: !this.props.sortOrder
 });
 }
 onSorted = () => {
 // get&set our sorting: field & way of sorting
 this.setState({
 isActive: true,
 sortOrder: !this.state.sortOrder
 });
 let sorted;
 this.state.sortOrder ? sorted = 'asc' : sorted = 'desc';
 this.props.onSortingChanged(this.props.field, sorted);
 }
 renderArrow() {
 // render sorting arrow: asc/desc
 if (this.props.isActive) {
 return this.state.sortOrder ? <span>↓</span> : <span>↑</span>
 }
 }
 render() {
 return (
 <div className={"table-header"} onClick={this.onSorted}>
 <span>{this.props.text}</span>
 {this.renderArrow()}
 </div>
 )
 }
}
export default TableHeader;

I'm new to React. I major in Angular. And for me it's realy not so obvious here... Do I use this props & state variables in a good way or do I need to refactor/change something?

Main goal is such: simple app with table, search & pagination. URL-state with query params should lead to a proper page, sorting etc when somebody copy-paste the url. All params in URL are optional (so I do not like the idea of using url like /:offset/:limit/:search etc).

My repository:

https://bitbucket.org/kot_matpockuh/react_test_app/src/master/

Also maybe I have any other wierd-code?

default

AltStyle によって変換されたページ (->オリジナル) /