How to reload/refresh a react-table component from my form component

Jatinder Thind picture Jatinder Thind · May 28, 2019 · Viewed 8.2k times · Source

I have two reactJS components:

  1. CustomerForm component with a form and form handling code.
  2. CustomerList component which lists the customers using react-table.

Both components are functional and I can use the form to add data to the database, and then fetch and display data in react-table.

But I can't figure out how to refresh the react-table data on successful form submission.

I am loading the components directly into an HTML page and using babel to process JSX. I am just starting our with reactJS and more used to PHP/jQuery development, so maybe I am approaching this wrong. Would appreciate any feedback.

My code:

CustomerForm

class CustomerForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {data: [], name: '', phone: '', nameErr: '', phoneErr: ''};
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }

    handleChange(event) {
        const target = event.target;
        const name = target.name;
        this.setState({[name]: event.target.value});
    }

    handleSubmit(event) {
        event.preventDefault();

        var self = this;

        axios.post(APP_URL + '/customer/add', {
            name: this.state.name,
            phone: this.state.phone
        }).then(function (response) {
            //console.log(response);
            var data = response.data;
            if (data.status === 0) {
                if (typeof data.payload === 'object') {
                    for (var key in data.payload) {
                        if (data.payload[key]) {
                            self.setState({[key]: data.payload[key]});
                        }
                    }
                }
            }
        }).catch(function (error) {
            console.log(error);
        });
    }

    render() {
        return (
                <div className="container mt-3">
                    <form onSubmit={this.handleSubmit}>
                        <div className="row">
                            <div className="col-6">
                                <div className="form-group">
                                    <label>Name</label>
                                    <input name="name" type="text" className={'form-control ' + (this.state.nameErr ? 'is-invalid' : '')} placeholder="Enter name"  value={this.state.value} onChange={this.handleChange} />
                                    <div className="invalid-feedback">{this.state.nameErr}</div>
                                </div>
                                <div className="form-group">
                                    <label>Email address</label>
                                    <input name="phone" type="text" className="form-control" placeholder="Enter phone"  value={this.state.value} onChange={this.handleChange} />
                                </div>
                                <button type="submit" className="btn btn-primary">Submit</button>
                            </div>
                        </div>
                    </form>
                </div>

                );
    }
}

const domContainer = document.querySelector('#CustomerFormContainer');
ReactDOM.render(e(CustomerForm), domContainer);

Customer List

class CustomerList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {data: [], loading: false, pages: null};
        this.fetchData = this.fetchData.bind(this);
    }

    fetchData(state, instance) {
        var self = this;

        this.setState({loading: true});

        axios.post(APP_URL + '/customer/index', {
            page: state.page,
            pageSize: state.pageSize,
            sorted: state.sorted,
            filtered: state.filtered
        }).then(function (response) {
            // handle success
            self.setState({
                data: response.data.payload,
                pages: 1,
                loading: false
            });
        }).catch(function (error) {
            // handle error
            console.log(error);
        }).finally(function () {
            // always executed
        });
    }

    render() {
        const {data, pages, loading} = this.state;
        return (
                <div className="container mt-3">
                    <ReactTable
                        columns={[
                                    {
                                        Header: "Name",
                                        accessor: "name"
                                    },
                                    {
                                        Header: "Phone",
                                        accessor: "phone"
                                    }
                                ]}
                        manual
                        data={this.state.data}
                        pages={this.state.pages} 
                        loading={this.state.loading} 
                        onFetchData={this.fetchData} 
                        defaultPageSize={10}
                        className="-striped -highlight"
                        />
                    <br />
                </div>
                );
    }
}

const domContainer = document.querySelector('#CustomerListContainer');
ReactDOM.render(e(CustomerList), domContainer);

Answer

Vencovsky picture Vencovsky · May 28, 2019

TL;DR

Render both components in a parent component. Lift the table state up and pass a function to the form that will call the function to fetch data.


First of all, one quick tip. Render only one component in one div and then nest all the other components inside.

In both files, you are rendering the component in diferent selectors

CustomerForm

const domContainer = document.querySelector('#CustomerFormContainer');
ReactDOM.render(e(CustomerForm), domContainer);

Customer List

const domContainer = document.querySelector('#CustomerListContainer');
ReactDOM.render(e(CustomerList), domContainer);

This isn't good because this way, it's not easy to share state and props between then.

What you should do is have a root component that render both components.

class App extends React.Component {
    render() {
        return (
            <CustomerForm />
            <CustomerList />
        )
    }
}

const domContainer = document.querySelector('#app');
ReactDOM.render(e(App), domContainer);

OBSERVATION I'm not sure what is the e function and why you are using, but I assume this won't change anything, but please, specify what it is.

Doing this, you can share state and props between components.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: [], loading: false, pages: null };
    this.fetchData = this.fetchData.bind(this);
    this.reloadData = this.reloadData.bind(this);
  }

  reloadData() {
    this.fetchData(state);
  }

  fetchData(state, instance) {
    var self = this;

    this.setState({ loading: true });

    axios
      .post(APP_URL + "/customer/index", {
        page: state.page,
        pageSize: state.pageSize,
        sorted: state.sorted,
        filtered: state.filtered
      })
      .then(function(response) {
        // handle success
        self.setState({
          data: response.data.payload,
          pages: 1,
          loading: false
        });
      })
      .catch(function(error) {
        // handle error
        console.log(error);
      })
      .finally(function() {
        // always executed
      });
  }
  render() {
    return (
      <div>
        <CustomerForm reloadData={reloadData} />
        <CustomerList data={data} pages={pages} loading={loading} fetchData={fetchData} />
      </div>
    );
  }
}

Here what I'm doing is called Lifting State Up. I'm getting the state of CustomerList to the parent component so you can reload the data when the form calls reloadData.

This way you also need to change CustomerList to get the data from this.props and in CustomerForm call reloadData when the submit is success.

Customer List

  render() {                                   // getting from props
    const { data, pages, loading, fetchData } = this.props;
    return (
      <div className="container mt-3">
        <ReactTable
          columns={[
            {
              Header: "Name",
              accessor: "name"
            },
            {
              Header: "Phone",
              accessor: "phone"
            }
          ]}
          manual
          data={data}
          pages={pages}
          loading={loading}
          onFetchData={fetchData}
          defaultPageSize={10}
          className="-striped -highlight"
        />
        <br />
      </div>
    );
  }

CustomerForm

Call this.props.reloadData() when the submit is success.

*I don't know when you want to reload the data, it's not clear when is a succes.