3

I'm having trouble setting the state of a component in React. The component is called "Search" and uses react-select. The full component is here:

class Search extends React.Component {
  constructor(props){
    super(props);
    let options = [];
    for (var x in props.vals){
      options.push({ value: props.vals[x], label: props.vals[x], searchId: x });
    };
    this.state = {
      inputValue: '',
      value: options
    };
  }

  handleChange = (value: any, actionMeta: any) => {
    if(actionMeta.action == "remove-value"){
      this.props.onRemoveSearch({ searchId: actionMeta.removedValue.searchId })
    }
    this.setState({ value });
  };

  handleInputChange = (inputValue: string) => {
    this.setState({ inputValue });
  };

  handleSearch = ({ value, inputValue }) => {
    this.setState({
      inputValue: '',
      value: [...value, createOption(inputValue)], // Eventually like to take this out...
    });
    this.props.onSearch({ inputValue });
  }

  handleKeyDown = (event: SyntheticKeyboardEvent<HTMLElement>) => {
    const { inputValue, value } = this.state;
    if (!inputValue) return;
    switch (event.key) {
      case 'Enter':
      case 'Tab':
        this.handleSearch({
          value,
          inputValue
        });
        event.preventDefault();
    }
  };

  render() {
    const { inputValue, value } = this.state;
    return (
        <div className="search">
            <div className="search__title">Search</div>
            <Tooltip
              content={this.props.tooltipContent}
              direction="up"
              arrow={true}
              hoverDelay={400}
              distance={12}
              padding={"5px"}
              >
              <CreatableSelect
                className={"tags"}
                components={components}
                inputValue={inputValue}
                isMulti
                menuIsOpen={false}
                onChange={this.handleChange}
                onInputChange={this.handleInputChange}
                onKeyDown={this.handleKeyDown}
                placeholder="Add filters here..."
                value={value}
              />
            </Tooltip>
        </div>
    );
  }
}

module.exports = Search;

You've probably noticed the strange thing that I'm doing in the constructor function. That's because I need to use data from my firebase database, which is in object form, but react-select expects an array of objects with a "value" and "label" property. Here's what my data looks like:

enter image description here

To bridge the gap, I wrote a for-in loop which creates the array (called options) and passes that to state.value.

The problem: Because I'm using this "for in" loop, React doesn't recognize when the props have been changed. Thus, the react-select component doesn't re-render. How do I pass down these props (either modifying them inside the parent component or within the Search component) so that the Search component will re-render?

1
  • 2
    If your component is not unmounting, the constructor will not be rerun. So your for in loop will not be rerun unless it unmounts. Another option would be to move the options into your render method, or add an additional lifecycle method of componentDidUpdate Commented Nov 16, 2018 at 3:12

1 Answer 1

2

I would suggest not using the value state. What you do is simply copying props into your state. You can use props in render() method directly.

I reckon you use the value state because you need to update it based on user actions. In this case, you could lift this state up into the parent component.

class Parent extends React.Component {
  constructor() {
    this.state = { value: //structure should be the same as props.vals in ur code };
  }
  render() {
    return (
      <Search vals={this.state.value}/>
    );
  }
}

class Search extends React.Component {
  constructor(props){
    super(props);

    this.state = {
      inputValue: '',
    };
  }

  render() {
    const { inputValue } = this.state;
    const { vals } = this.props;
    let options = [];
    for (var x in vals){
      options.push({ value: vals[x], label: vals[x], searchId: x });
    };
    return (
        <div className="search">
            <div className="search__title">Search</div>
            <Tooltip
              content={this.props.tooltipContent}
              direction="up"
              arrow={true}
              hoverDelay={400}
              distance={12}
              padding={"5px"}
              >
              <CreatableSelect
                value={options}
              />
            </Tooltip>
        </div>
    );
  }
}

module.exports = Search;
Sign up to request clarification or add additional context in comments.

3 Comments

Yes, I am using the value state because the react-select component relies on it. Could you perhaps give a short example of what you mean by "lifting" the state up to my parent component? How could I accomplish that? Thank you.
Many things depend on the "value" state in my Search component so I'm a little unclear about how to lift it up.
Please see my updated answer for a brief demo. Without insights into ur code, I can't provide a working demo. You may also need to update your event handler functions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.