6

I have a React Component like shown bellow (some parts are ommited) and I'm using redux for state management. The getRecentSheets action contains an AJAX request and dispatches the response to redux which updates state.sheets.recentSheets with the response's data.

All this works as expected, but on building it throws warning about useEffect has a missing dependency: 'getRecentSheets'. But if I add getRecentSheets to useEffect's dependency array it starts to rerun indefinitely and thus freezes the app.

I've read React documentation about the useEffect hook https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies but it doesn't provide a good example for such usecase. I suppose it is something with useCallback or react-redux useDispatch, but without examples I'm not sure how to implement it.

Can someone please tell me what the most concise and idiomatic way to use redux action in useEffect would be and how to avoid warnings about missing dependencies?

import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; import SheetList from '../components/sheets/SheetList'; import { getRecentSheets } from '../store/actions/sheetsActions'; const mapStateToProps = (state) => { return { recentSheets: state.sheets.recentSheets, } } const mapDispatchToProps = (dispatch) => { return { getRecentSheets: () => dispatch(getRecentSheets()), } } const Home = (props) => { const {recentSheets, getRecentSheets} = props; useEffect(() => { getRecentSheets(); }, []) return <SheetList sheets={ recentSheets } /> }; export default connect(mapStateToProps, mapDispatchToProps) (Home); 
1
  • 1
    You may have an easier time if you use redux hooks instead of connect. You just have one useDispatch and as many useSelectors as you need. And I believe that useDispatch will not return a new function everytime making it effect dependency safe. Commented Mar 4, 2020 at 0:13

3 Answers 3

6

After all, it seems that correct way will be as follows:

// ... import { useDispatch } from 'react-redux'; import { getRecentSheets } from '../store/actions/sheetsActions'; const Home = props => { const dispatch = useDispatch(); useEffect(() => { dispatch(getRecentSheets()); }, [dispatch]) // ... }; 

This way it doesn't complain about getRecentSheets missing in dependencies array. As I understood from reading React doc on hooks that's because it's not defined inside the component. Though I'm new to frontend and I hope I didn't mess something up here.

Sign up to request clarification or add additional context in comments.

Comments

1

Passing an empty array in your hook tells React your hook function will not have any dependent values from either props or state.

useEffect(() => { getRecentSheets(); }, []) 

The infinite loop arises when you declare the dispatcher as a dependency on the hook. When the component is initialized, props.recentSheets hasn't been set, and will rerender once you make your AJAX call.

useEffect(() => { getRecentSheets(); }, [getRecentSheets]) 

You could try something like this:

const Home = ({recentSheets}) => { const getRecentSheetsCallback = useCallback(() => { getRecentSheets(); }) useEffect(() => { getRecentSheetsCallback(); }, [recentSheets]) // We only run this effect again if recentSheets changes return <SheetList sheets={ recentSheets } /> }; 

No matter how many times Homes re-renders, you retain the memoized function to your dispatch call.

Alternatively, you may have encountered find similar patterns utilizing local state and then make your effect "depend" on sheets.

const [sheets, setSheets] = useState(recentSheets) 

Hope this helps

Comments

0

I would add a check to see if recentSheets exists or not, using that as my dependency.

useEffect(() => { if (!recentSheets) getRecentSheets(); }, [recentSheets]) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.