Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React useEffect Hook when only one of the effect's deps changes, but not the others

I have a functional component using Hooks:

function Component(props) {   const [ items, setItems ] = useState([]);    // In a callback Hook to prevent unnecessary re-renders    const handleFetchItems = useCallback(() => {     fetchItemsFromApi().then(setItems);   }, []);    // Fetch items on mount   useEffect(() => {     handleFetchItems();   }, []);    // I want this effect to run only when 'props.itemId' changes,   // not when 'items' changes   useEffect(() => {     if (items) {       const item = items.find(item => item.id === props.itemId);       console.log("Item changed to " item.name);     }   }, [ items, props.itemId ])    // Clicking the button should NOT log anything to console   return (     <Button onClick={handleFetchItems}>Fetch items</Button>   ); } 

The component fetches some items on mount and saves them to state.

The component receives an itemId prop (from React Router).

Whenever the props.itemId changes, I want this to trigger an effect, in this case logging it to console.


The problem is that, since the effect is also dependent on items, the effect will also run whenever items changes, for instance when the items are re-fetched by pressing the button.

This can be fixed by storing the previous props.itemId in a separate state variable and comparing the two, but this seems like a hack and adds boilerplate. Using Component classes this is solved by comparing current and previous props in componentDidUpdate, but this is not possible using functional components, which is a requirement for using Hooks.


What is the best way to trigger an effect dependent on multiple parameters, only when one of the parameters change?


PS. Hooks are kind of a new thing, and I think we all are trying our best to figure out how to properly work with them, so if my way of thinking about this seems wrong or awkward to you, please point it out.

like image 589
darksmurf Avatar asked Apr 17 '19 09:04

darksmurf


People also ask

How do I fix React hook useEffect has missing dependencies?

To solve the "react hook useeffect has a missing dependency" warning, you can: Add the missing dependency. Move the dependency inside the useEffect hook. Disable the ESLint rule.

What happens if you don't specify the dependencies list in useEffect?

If you don't specify it, the effect runs after each render. If it's empty ( [] ), the effect runs once, after the initial render.

Can you make useEffect hook to run only on component update and not when component mounted?

React's useEffect Hook doesn't come with a feature to run it only on update, however, this custom hook should help you to accomplish it.

How do I make useEffect render only once?

Side Effect Runs Only Once After Initial Render You can pass an empty array as the second argument to the useEffect hook to tackle this use case. useEffect(() => { // Side Effect }, []); In this case, the side effect runs only once after the initial render of the component.

What is useeffect in react hooks?

Syntax useEffect(()=>{},[]); ()=>{} − Function passed to this hook [ ] − It tells the hook when to re-render the component. For example − [props] − If props values are changed then this hook is called again. [ ] − This hook will be called once only when the component is rendered to the screen. Example

What is the use of componentdidupdate hook in react?

This hook is used to set up subscriptions, handle side-effects or to send analytics data to the server. It is the combination of componentDidMount, componentDidUpdate and componentWillUnmount methods of class-based components. The function passed to this hook will run only after the component is rendered.

What is [props] hook in react?

For example − [props] − If props values are changed then this hook is called again. [ ] − This hook will be called once only when the component is rendered to the screen. In this example, we will build a React application that displays the message when the child component is mounted in the DOM.

What is a side effect in react?

Data fetching, setting up a subscription, and manually changing the DOM in React components are all examples of side effects. Whether or not you’re used to calling these operations “side effects” (or just “effects”), you’ve likely performed them in your components before.


1 Answers

The React Team says that the best way to get prev values is to use useRef: https://reactjs.org/docs/hooks-faq.html#how-to-get-the-previous-props-or-state

function Component(props) {   const [ items, setItems ] = useState([]);    const prevItemIdRef = useRef();   useEffect(() => {     prevItemIdRef.current = props.itemId;   });   const prevItemId = prevItemIdRef.current;    // In a callback Hook to prevent unnecessary re-renders    const handleFetchItems = useCallback(() => {     fetchItemsFromApi().then(setItems);   }, []);    // Fetch items on mount   useEffect(() => {     handleFetchItems();   }, []);    // I want this effect to run only when 'props.itemId' changes,   // not when 'items' changes   useEffect(() => {     if(prevItemId !== props.itemId) {       console.log('diff itemId');     }      if (items) {       const item = items.find(item => item.id === props.itemId);       console.log("Item changed to " item.name);     }   }, [ items, props.itemId ])    // Clicking the button should NOT log anything to console   return (     <Button onClick={handleFetchItems}>Fetch items</Button>   ); } 

I think that this could help in your case.

Note: if you don't need the previous value, another approach is to write one useEffect more for props.itemId

React.useEffect(() => {   console.log('track changes for itemId'); }, [props.itemId]); 
like image 89
David Táboas Avatar answered Oct 06 '22 07:10

David Táboas