Search code examples
javascriptreactjsfetchreact-lifecycle

Assignment of value in ComponentDidMount instead of constructor function in React


Following is my code (which is working fine) in which I am able to sort list on the basis of input provided in text box. In constructor method I declared my states like this -

this.state = {
      data: ["Adventure", "Romance", "Comedy", "Drama"],
      tableData: []
    };

In componentDidMount method I assign the data key state in tableData.

  componentDidMount() {
    this.setState({
      tableData: this.state.data
    });
  }

My question is - Is it the correct way of doing this as somehow I myself not feeling confident about this code quality (Initializing the tableData as [] and then setting tableData: this.state.data in componentDidMount method ). Let me know if I can improve this also what will change if I fetch the data from an API which is the best place to initialize and use in an app.

Working Code Example - https://codesandbox.io/s/k9j86ylo4o

Code -

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: ["Adventure", "Romance", "Comedy", "Drama"]
    };
    this.handleChange = this.handleChange.bind(this);
  }

  refineDataList(inputValue) {
    const listData = this.state.data;
    const result = listData.filter(item =>
      item.toLowerCase().match(inputValue.toLowerCase())
    );
    this.setState({
      data: result
    });
  }

  handleChange(e) {
    const inputValue = e && e.target && e.target.value;
    this.refineDataList(inputValue);
  }
  render() {
    return (
      <div className="App">
        <h3>DATA SEARCH</h3>
        <div className="form">
          <input type="text" onChange={this.handleChange} />
        </div>
        <div className="result">
          <ul>
            {this.state.data &&
              this.state.data.map((item, i) => {
                return <li key={i}>{item}</li>;
              })}
          </ul>
        </div>
      </div>
    );
  }
}

Solution

  • You are doing great, but you are right there is a way to do it in better way, handle two point of truth is difficult to maintain, so you should have only one data array with the words that you need, so the way you should filter the values is by creating a filter variable into state to store the current word to be filtered, so you should add something like

    // in the constructor function
    constructor(props) {
      super(props);
      this.state = {
        data: ["Adventure", "Romance", "Comedy", "Drama"],
        filter: ""
      }
    }
    
    // create a filter function
    getFilteredResults() {
      const { filter, data } = this.state;
      return data.filter(word => String(word).toLowerCase().match(filter));
    }
    
    // and finally into your render function
    render() {
      return (
        <div>
          {this.getFilteredResults().map((word) => (
            <div>{word}</div>
          ))}
        </div>
      );
    }
    

    Obviously remember to update your handleChange function, like so

    handleChange(e) {
      const inputValue = e && e.target && e.target.value;
      this.setState({ filter: inputValue });
      //this.refineDataList(inputValue);
    }
    

    In that way you will maintain only one point of truth, it will work as expected.

    Note: we use String(word).toLowerCase() to ensure that the current word is actually a string, so we can avoid the toLowerCase is not function of undefined error, if for some reason word is not a string.