2

I was trying to update the state after user selects the dropdown, however, the selected option is never changed. See the gif -- https://recordit.co/KH2Pqn34bp.

I am confused that ideally, after using setFilterOptions to update state, it's supposed to re-render this component with a new value, but it doesn't happen. Could anyone help take a look? What am I missing here? Thanks a lot!

Example code on sandbox -- https://codesandbox.io/s/react-select-default-value-forked-1ybdk?file=/index.js

const SearchFilter = () => {
    const [filterOptions, setFilterOptions] = useContext(SearchFilterContext);  
    let curSort = filterOptions['sortType'] || DEFAULT_SORT_OPTION;
    const handleSortChange = (option) => {
        setFilterOptions(previous => Object.assign(previous, { 'sortType': option }))
    };
    return (
        <span className='filter-container'>
            <Select options={SORT_TYPE_OPTIONS} value={curSort} onChange={handleSortChange}/>
       </span>
    );
};
Haoyu Chen
  • 1,760
  • 1
  • 22
  • 32
  • Can you provide a minimal, reproducible example? My guess is that you are not using the `onChange` prop inside the `Select` component. – Yousaf Dec 07 '20 at 17:33
  • @Yousaf I tried to get this on sandbox -- https://codesandbox.io/s/react-select-default-value-forked-1ybdk?file=/index.js, hopefully this would be helpful. – Haoyu Chen Dec 07 '20 at 17:39
  • @Yousaf could you explain a little bit more on not using `onChange` prop? Because `handleSortChange` is basically the onChange method, which contains `setFilterOptions `. – Haoyu Chen Dec 07 '20 at 17:43

2 Answers2

1

Couple of problems in your code:

  1. To set the default value of the Select component, you have written some unnecessary code. Instead, you could just use the defaultValue prop to set the default value of the Select component.

    <Select
       options={OPTIONS}
       defaultValue={OPTIONS[0]}
       onChange={handleSortChange}
    />
    
  2. You are mutating the state directly. Object.assign(...) returns the target object. In your case, the target object is the previous state.

    Instead of returning the new state object, you mutate the state directly and return the previous state object which prevents a re-render.

    Using the spread-syntax, you can update the state correctly as shown below:

    const handleSortChange = (option) => {
       setFilterOptions({ ...filterOptions, sortType: option });
    };
    

Following code fixes the above mentioned problem in your component:

const SearchFilter = () => {
  const [filterOptions, setFilterOptions] = useState({});

  const handleSortChange = (option) => {
    setFilterOptions({ ...filterOptions, sortType: option });
  };

  return (
    <span className="filter-container">
      <Select
        options={OPTIONS}
        defaultValue={OPTIONS[0]}
        onChange={handleSortChange}
      />
    </span>
  );
};
Yousaf
  • 27,861
  • 6
  • 44
  • 69
0

The reason you're not seeing a change is that this functional component will only re-render when it sees that your state changed (based on your sandbox script). At the moment when you use Object.assign(previous, { 'sortType': option}) you're changing the sortType property in the object but the object itself doesn't change and so the functional component doesn't see a change.

We can resolve this by using either Object.assign({}, previous, { 'sortType': option}) which will create a NEW object with the previous state attributes and the changed sortType (the first param to Object.assign is the object where the following object properties will get copied into. if we use an empty object that's the equivalent of a new object) or we can use a spread operator and replace it with ({...sortType, 'sortType': option}) which will also create a new object that the functional component will recognize as a changed state value.

const handleSortChange = (option) => {
    setFilterOptions(previous => Object.assign({}, previous, { 'sortType': option }))
};

or

const handleSortChange = (option) => {
    setFilterOptions(previous => ({...previous, 'sortType': option})
};

Keep in mind these are shallow object copies.

Jonathan Beadle
  • 418
  • 2
  • 7