Some rough notes on React after playing with it for a day

Fri Feb 19 2021

tags: 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.

Screenshot of the very ugly page

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 a setState function that mutates that state 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 in useState. What's dispatch and how is it different from setState?

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.

Bibliography