Latest news about Bitcoin and all cryptocurrencies. Your daily crypto news habit.
The problem is as old as it gets:
- thereâs a tree of components
- two of them need to talk.
One obvious solution is to use some sort of state management library, like Redux or MobX. But letâs say the above problem statement is all there is to itâââthe app has no other state, itâs a sea of silent, disconnected components. It just so happens that two of them need to share one variable.
The next shot would be context. Thatâs what itâs for, right? Context provides a shared variable to a tree of components! But unfortunately context is not freeâââone component needs to be designated as the holder of the value of this context. Because in React, if something is a state (it changes), it must live in a component.
Adding context introduces another component (or at least forces a hierarchy), and thatâs not what we desire here. We want two lonesome components to have a long-distance call. Period.
Hooks to the rescue
Fortunately, there are hooks in React! Hooks are a way to tie a component to React runtime (hook into, right?).
This was not possible with functional components before. They were treated by React as pure functions: take in some props, spit out DOM elements, end of story. A functional component did not have a lifecycleâââit was not possible to e.g. do something only on first render, or hold state. Data in, HTML out, thatâs all there was to it.
With hooks, a functional component can be impure. Some state can be added to a component, or an action performed only once (like fetching data). Hooks are amazing and take a lot of pain out of React development, while simplifying the way we use React.
Just a hook
An approach I really like when writing code is starting from the API design. When you imagine the code youâre about to use already exists, you can focus on this fantasy API. You wonât be forced into one by some implementation details or constraints.
So in this case, what we want is a hook, and the hook should provide a way to read and write to the shared variable:
const { answer, setAnswer } = useAnswer()
Nice! Thatâs basically all a component should need to interact with the shared variable. That was quick. As you mightâve noticed this approach of fantasy-API-first ties in nicely with Test Driven Development. First we imagine a perfect world, write some tests to express how it should work, and thenâââat lastâââcreate it.
So letâs do that. We start with a variable:
let answer
This is the source of truth. This is just a variable defined in a fileâââas simple as it gets. Now letâs get a Set for the state updaters. Each component that uses the hook will have its own updater function, and will get notified separately.
const stateUpdaters = new Set()
Why a Set?Sets have this nice property that they disallow duplicate values, which will come in handy here. Next up is the updating function for the value, passed to the componentâââthe âwriteâ part of this whole business:
const setValue = newValue => { value = newValue stateUpdaters.forEach(fn => fn(value))}
Again, weâre dealing with pure JavaScript here. Itâs just a function. And finally, the part where any React comes into play, namely the setState hook (which comes with React):
export default () => { const [, setState] = useState(); stateUpdaters.add(setState); return [value, setValue];}
Thatâs all! And here you can see it all come together:
One thing though!
If youâre a bit experienced with React you might have noticed one problem with this code. Since each caller component triggers registration of an updating function, it should also deregister it when it un-mounts! I encourage you to fork this code sample and handle that scenario. Hint: it involves another hook, called useEffect đ.
Why not?
Letâs think of some problems a developer might see with this approach:
Context was out because it introduces a third partyâââa source of truth, while this solution also proposes one, in the form of a variable!
Yep, but context forces a tree hierarchy, and the source of truth is a component. Here, itâs just a variable. Which simplifies the whole matter greatly and avoids polluting the component tree.
This is not idiomatic React! And this mutated variable is an abomination to the functional sprit of React!
Programming is often about finding the right tool for the job. Introducing a more complex state management solution while all that was needed is a variable shared between components is unnecessary.
Even in a big Redux-powered app, local component state has its uses (e.g. selected tab in a tab container). This approach of a shared mutable variable is just something between local component state and global app state.
This does not scaleâŠ
Exactly! If all you need is to display something in multiple places, and the component tree stands in the way, this approach might be better than adding 20 lines of logic to the global state management code.
Should it scale?
You might like this approach, but want to take it the next level. Store a lot of data instead of just a number. Use a reducer-like logic to make updating more wieldy. And then just use that hook as the state management solution.
Well, as it often happens in JavaScript-land, thereâs tons of packages that do just thatâââsimplify state management with hooks. While I personally prefer Redux for this job, these are interesting propositions that solve the problem of global state in a much more âintuitiveâ way.
Frankly, I would not use global mutated state for anything bigger than a single variable. And even then Iâd feel a bit squeamish using it. If a state is supposed to be shared across the component tree, itâs a hint that it probably belongs to the global state.
But I hope that despite the anti-recommendation of the whole concept Iâve managed to show you an interesting use case for React hooks! Thatâs what they are for in the endâââbuilding blocks for composition and experimentation. And itâs also why I love React.
Long-distance React hooks đđŁ was originally published in HackerNoon.com on Medium, where people are continuing the conversation by highlighting and responding to this story.
Disclaimer
The views and opinions expressed in this article are solely those of the authors and do not reflect the views of Bitcoin Insider. Every investment and trading move involves risk - this is especially true for cryptocurrencies given their volatility. We strongly advise our readers to conduct their own research when making a decision.