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 ofcreateUseState
.
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
- Part 1, Element
- Part 2, useEffect
- Part 3, useState
- Part 4, Hook
- Part 5, Custom Hook
- Part 6, useContext
- Part 7, State Management
- Part 8, Four-body Problem <- YOU ARE HERE
The ideas used above are heavily borrowed and simplified from Zustand, useSWR and Recoil.