简体   繁体   中英

With React hooks how to update 2 hooks together, but use prevState of one in other

In the code below, when I call _toggleSearch I update 2 hooks simultaneously.

toggleSearchIsVisible is a simple boolean, however, setActiveFilter hook must pass in the previous searchIsVisible state.

How can I be sure the other state hasn't updated yet when passed in? Or how else should I restructure everything? Previously with setState I could've passed in the previous state easily.

const [activeFilter, setActiveFilter] = useState('SHOW_ALL');
const [searchIsVisible, toggleSearchIsVisible] = useState(false);

const _toggleSearch = () => {
  setActiveFilter(searchIsVisible ? 'SHOW_SEARCH' : 'SHOW_ALL');
  toggleSearchIsVisible(!searchIsVisible);
};

I've gone with this for now:

const _toggleSearch = () => {
  if (searchIsActive) {
    setActiveFilter('SHOW_ALL');
    toggleSearchIsActive(false);
  } else {
    setActiveFilter('SHOW_SEARCH');
    toggleSearchIsActive(true);
    updateSearchValue('');
  }
};

Any other suggestions appreciated!

Try using the useEffect hook to deal with the side effects of toggling the searchIsVisible boolean.

Here is an example:

const _toggleSearch = () => {
  toggleSearchIsVisible(!searchIsVisible);
};

useEffect(() => {
  setActiveFilter(searchIsVisible ? 'SHOW_ALL' : 'SHOW_SEARCH');
}, [searchIsVisible]);

You can see that I also passed [searchIsVisible] to the useEffect hook to optimize performance.

This will ensure that useEffect is only triggered when searchIsVisible is updated: https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects


Here is a demo: https://codesandbox.io/s/l2901jr68l

I hope this helps.

Note:

Also I switched the order of SHOW_ALL and SHOW_SEARCH in your conditional, because it would seem that SHOW_SEARCH should be displayed when searchIsVisible is false , and SHOW_ALL should be displayed when searchIsVisible is true , but if that's not the case just switch the order back.

If you encounter a situation about syncing two states, take a step back and see if one of the states can be derived from the other. It seems that in this case the two states you have that are essentially related, and one of them can indeed be derived from the other. So you actually don't need two state values, you only need one. Derived state should not be part of state as that leads to duplication of state and then you have to sync up the state values, which is what you are asking in your question.

In the examples below, I demonstrate how you can use either activeFilter or searchIsVisible as the single state to retain.

If you retain activeFilter , the value of searchIsVisible is derived from the and vice versa. The key is to set searchIsVisible in depending on the value of activeFilter .

activeFilter as state

 const App = () => { const [activeFilter, setActiveFilter] = React.useState('SHOW_ALL'); const searchIsVisible = activeFilter !== 'SHOW_ALL'; const _toggleSearch = () => { setActiveFilter(activeFilter === 'SHOW_ALL' ? 'SHOW_SEARCH' : 'SHOW_ALL'); }; return ( <div> <p>Active Filter: {activeFilter}</p> <button onClick={_toggleSearch}>Toggle Search</button> {searchIsVisible && <div> <input placeholder="Search" /> </div>} </div> ); }; ReactDOM.render(<App />, document.querySelector('#app')); 
 <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script> <div id="app"></div> 

searchIsVisible as state

 const App = () => { const [searchIsVisible, setSearchIsVisible] = React.useState(false); const activeFilter = searchIsVisible ? 'SHOW_SEARCH' : 'SHOW_ALL'; const _toggleSearch = () => { setSearchIsVisible(!searchIsVisible); }; return ( <div> <p>Active Filter: {activeFilter}</p> <button onClick={_toggleSearch}>Toggle Search</button> {searchIsVisible && <div> <input placeholder="Search" /> </div>} </div> ); }; ReactDOM.render(<App />, document.querySelector('#app')); 
 <script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script> <div id="app"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM