When multiple values are closely related and change at the same time, it can make sense to group these values in a collection like an object or array. Packaging data together can also add complexity to the code responsible for managing that data. Therefore, it is a good idea to separate concerns by managing different data with different Hooks.
Compare the complexity here, where data is bundled up into a single object:
// Handle both position and menuItems with one useEffect hook. const [data, setData] = useState({ position: { x: 0, y: 0 } }); useEffect(() => { get('/menu').then((response) => { setData((prev) => ({ ...prev, menuItems: response.data })); }); const handleMove = (event) => setData((prev) => ({ ...prev, position: { x: event.clientX, y: event.clientY } })); window.addEventListener('mousemove', handleMove); return () => window.removeEventListener('mousemove', handleMove); }, []);
To the simplicity here, where we have separated concerns:
// Handle menuItems with one useEffect hook. const [menuItems, setMenuItems] = useState(null); useEffect(() => { get('/menu').then((response) => setMenuItems(response.data)); }, []); // Handle position with a separate useEffect hook. const [position, setPosition] = useState({ x: 0, y: 0 }); useEffect(() => { const handleMove = (event) => setPosition({ x: event.clientX, y: event.clientY }); window.addEventListener('mousemove', handleMove); return () => window.removeEventListener('mousemove', handleMove); }, []);
It is not always obvious whether to bundle data together or separate it, but with practice, we get better at organizing our code so that it is easier to understand, add to, reuse, and test!
Instructions
At the moment, this code seems to work just fine. There are three different network requests being made in a single effect and lots of different, unrelated data being managed in a single state variable. Let’s get to work breaking these single useState()
and useEffect()
calls into separate, simpler hooks. Doing so will make this code easier to understand, build on top of, and reuse as we continue to improve our application!
Begin refactoring this component:
- Use a separate State Hook for
menu
,newsFeed
, andfriends
. - Use these new state setters instead of
setData()
in the effect. - Simplify our JSX to use these new state variables instead of
data
.
Promise.all()
was helpful to us when we had all of our data bundled up in a single object. It called all backend services, and when they all sent back responses, we could then call our state setters with the responses.
Because we are now managing these values separately, we can remove this complexity! In our effect, call the get()
function three times for the three different data collections that our component wants to render, without using Promise.all()
any more.
Now that we have three separate backend calls, let’s continue to separate concerns by splitting each of these into three separate Effect Hooks!
Each useEffect()
call is working with a corresponding useState()
call. Let’s reorganize our code to show this relationship more clearly, making the logic easier to read and reuse!
For each of these three data collections, group the related State Hook and the Effect Hook next to one. This will help to make it clear which Hooks are working together to manage each separate data model.