Some rough notes on React after playing with it for a day
Fri Feb 19 2021tags: notes draft programming software engineering front end react
Introduction
I'm preparing for an upcoming interview where I'm expected to know React. As practice I built a very ugly, very silly page that allows you to customise your food to the gram (some names considered and rejected: "Food for Autists", "Macro Madlads") last night in a couple of hours.
lieu, [19.02.21 13:06] A website that allows you to build your own bowl (carbs, protein, veg, etc) but the macros are automatically updated
lieu, [19.02.21 13:06] so you can pick exactly how many grams of chicken breast and pasta you want
lieu, [19.02.21 13:06] Then pay and then we offer islandwide delivery within an hour's time
lieu, [19.02.21 13:06] Carbs: rice (brown/white), pasta (0g is an option) Protein: chicken breast, oven baked salmon, beef steak, tofu, egg Vegetables: random salad vegetable, kimchi, broccoli, edamame, cherry tomatoes. Price: base bowl/delivery cost + all items sold by weight, carbs and protein accurate to the gram.
The idea is not that important: the main objective here is to practice writing clean, idiomatic, modern React code. GH repo here.
How do you pass parameters into a callback function?
Suppose you have the following React components
where a child element calls a callback function
onChange
function Parent() {
function handleChange() {
// do something...
}
return <Child handleChange={handleChange}/>
}
function Child() {
return <input onChange={props.handleChange}/>
}
But what happens if you want to pass some parameters to the parent function? I was stuck on this for quite a while until Nick pointed this out to me. Don't think of passing parameters into a callback function per se; rather, create a higher-order function that then calls the parent's callback function.
function Parent() {
function handleChange(value) {
// do something with the value
}
return <Child handleChange={handleChange}/>
}
function Child() {
return <input onChange={(e) => handleChange(e.target.value)}/>
}
[?] Although I wonder if handleChange
would be already implicitly called
with e
as the argument...
Hooks TL;DR
- useState: takes in an initial state object,
returns a
state
object and asetState
function that mutates thatstate
object. - useEffect: takes in a function that usually performs side effects. That function may return a function. If so, the _returned _function is run when the
- useReducer: takes in an initial
reducer
function and an initial state and returns[state, dispatch]
.state
is straightforward, just like the one inuseState
. What'sdispatch
and how is it different fromsetState
?
Internally, Hooks are implemented as linked lists. When you call useState, we move the pointer to the next item. When we exit the component’s “call tree” frame, we save the resulting list there until the next render.
(source: React as a UI runtime)
What is the dispatch
method in the useReducer
hook?
The useReducer section of the Hooks Reference API writes:
useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);
An alternative to useState. Accepts a reducer of type (state, action) => newState, and returns the current state paired with a dispatch method. (If you’re familiar with Redux, you already know how this works.)
I'm not familiar with Redux, and I don't know how this works.
The reducer function is simple enough:
it's a function that takes in a state
and an action
object
and returns a new state object.
But what exactly is the function signature of dispatch
?
Well, it turns out that another section of the React docs
Building Your Own Hooks
has it.
The dispatch
function simply calls the provided reducer
function on the state
to get the new state object, nextState
.
It then mutates the state using setState(nextState)
.
function useReducer(reducer, initialState) {
const [state, setState] = useState(initialState);
function dispatch(action) {
const nextState = reducer(state, action);
setState(nextState);
}
return [state, dispatch];
}
Here's how we would use this dispatch
function:
function Todos() {
const [todos, dispatch] = useReducer(todosReducer, []);
function handleAddClick(text) {
dispatch({ type: 'add', text });
}
// ...
}
You can think of dispatch
as a "higher-level setState
":
while setState
merely replaces one state with another,
dispatch
allows us to specify exactly how we want to
setState
according to the reducer
function.
What's useReducer
for? Why use it over useState
The difference between useReducer
and useState
is that useReducer
allows you to pass it a reducer
function.
useReducer
also returns a dispatch
function that is the equivalent
of setState
.
Why would you use dispatch? Why not just pass the setState
function
down as props directly? You can certainly do that, but there are two advantages
to using dispatch.
Firstly, using dispatch
allows us to simplify complex updating functions,
as well as encapsulate all of the logic in one single reducer
function.
If you didn't have that function each child component would have to implement
their own function to create a new state before setting it.
With the dispatch pattern you can simply
pass it some parameters and the reducer
function will handle it.
[TODO] (... i'm not entirely convinced. come up with an example where it's obvious
that the dispatch method is significantly better than just passing setState down)
Speaking of passing props:
You can use dispatch with useContext
to avoid passing props many levels down.
It's a way for deeply nested components
to mutate the state of the parent which might be many levels up.
I remember first writing React in 2015 and thinking that it was very
very leceh to have to pass props down.
[TODO] (but again it's unclear why I can't pass the setState function down with context)
But what is useContext
?
What is context?
[TODO]
React's Context API is a mechanism for making a single user-provided value available to a subtree of components. Any component inside of a given
<MyContext.Provider>
can read the value from that context instance, without having to explicitly pass that value as a prop through every intervening component.