Understanding Hooks Part 8 — Four-body Problem

Fang Jin
4 min readMay 16, 2021

What’s behind the legendary React Hooks? From time to time, I wonder.

In Part 7 we talked about a single state management system of our own. In this Part 8, we’d like to put it in use and see how it can be applied to real problems, especially when we encounter the four-body (or N body) problem.

Typically a site starts with App that has a Header and multiple Routes.

Problem 1. You go to Profile route and change the user display name, and expect it to be reflected in Header.

The four-body is Profile , Routes , App and Header.

Problem 2. You go to Search route and fetch queried Results and then click one to view Detail route. You then go back to Search to expect to continue with the previous search Results.

The four-body is Results, Search, Routes and Detail.

Problem 3. You go to Listing route and select one to download the Detail content.

The four-body is Listing, Routes, Detail and Download.

Problem 4. You go to Detail route and want to act upon it by first displaying a Modal to confirm that action.

The four-body is Detail, Routes, App and Modal .

These problem all demonstrate that, pages (or parts of the page) can become twisted in a way that their relationship is not linear anymore. Therefore where the state should be allocated becomes a big time problem. And it becomes more challenging to solve when the number of components involved approaches to four. This is why I refer it as four-body (or N-body when N ≥ 4).

State

Let us try Problem 4 first. The Modal, quite clear to us from the picture, is a public service to the site. Which means it can be reached by any component.

const useModal = createUseState({
on: false,
title: "Are you sure?",
onClose: () => {}
})

If we model the state, it should have an on flag to indicate if it’s opened, and custom data such as the title and the callback onClose. When we reach Detail route, we can access this connected state via useModal.

const DetailComponent = () => {
const [yesNoModal, setYesNoModal] = useModal()
const onActionConfirmed = () => { setYesNoModal({ on: false }) }
const onAction = () = > {
setYesNoModal({ on: true, onClose: onActionConfirmed })
}
}

Believe it or not, useModal is composed of both local and global state to behavior as connected. For details, please refer to Part 7 for the implementation of createUseState.

Component

Put the Modal component along with App this time, instead of inside DetailComponent.

const Index = () => {
return (
<>
<Modal />
<App />
</>
)
}
const Modal = () => {
const [data, setData] = useModal()
if (!data || !data.on) return null
const { title, onClick } = data
return (
<div className="modal">
<h1>{title}</h1>
<button onClick={onClick}>Ok</button>
</div>
)
}

Modal appears when on flag is toggled. It is directly controlled, but if we want to take over this control, we can export open to make sure only one modal can open at a time.

const useSiteModal = () => {
const [data, setData] = useModal()
const open = (...props) => {
if (data.on) return
setData({ on: true, ...props })
}
return { data, setData, open }
}
const DetailComponent = () => {
const { open } = useSiteModal()
const onAction = () = > { open(...) }
}

Adding more custom data or logic is relatively more straightforward from now on. It’s like data as a service in a classical way.

Solutions

Ok, let’s take a look at other problems earlier, see if we can solve it in the similar way.

Problem 1. useUser can be created and consumed by both Header and Profile. Profile route can set the new user profile, which should get validated (via api) and saved, and sent for Header to display it.

Problem 2. useSearchResults can be created. Upon Search the new results is set so when you return to it any time, Results can display them. In order to invalidate, App can consume it during initialization to clear the results.

Problem 3. useDownloadDetail can be created as a service. In Listing clicking each record triggers it. And when you go to Detail/Download route, it can be used again.

Problem 4. useGlobalModal is created as a service. Any component can trigger it. We allow Modal to be implemented outside App. And we can control the number of the modals on the screen at the same time.

TL;DR

Four-body (or N-body) problem needs to be addressed by a state management system. The generic solution is to promote the shared data (or logic) to a higher level, and then use it as a service.

Index

The ideas used above are heavily borrowed and simplified from Zustand, useSWR and Recoil.

--

--

Fang Jin

#OpenToWork Front-end Engineer, book author of “Designing React Hooks the Right Way” sold at Amazon.