Harrison Cramer
Harrison Cramer

Reputation: 4506

Setting State with Objects from Firebase

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?

Upvotes: 3

Views: 388

Answers (1)

tingxuanz
tingxuanz

Reputation: 426

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;

Upvotes: 2

Related Questions