DEV Community

so2liu
so2liu

Posted on

Try to write more advanced React

After learning React Hooks, useState, useEffect, useContext; redux and redux-thunk/redux-saga, mobx; some UI Lib, you may feel losing direction, just like me.

This article is about what could be helpful for improving your react skill.

Everything comes from indirection

We can solve any problem by introducing an extra level of indirection.

React contains already a few directions somehow:

  • useState is a simplify of useReducer
  • useMemo and useCallback can be implemented by useRef

However, if we regard these hooks as a default base layer, the hooks can be divided to six directions:

  1. base build-in layer from React official.
  2. hooks to simplify the state update, like immer.js for immutability.
  3. Use "state + behavior" concept, build a complex context by declaration.
  4. Encapsulation of data structure, like manipulation of arrays.
  5. Encapsulation of scene, like padination arrays, multiple checkbox.
  6. Implement to real scene.

Use immer.js to update state

Problem: Hard to update a state deep in a object when you want to keep the immutability.

const newValue = {
    ...oldValue,
    foo: {
        ...oldValue?.foo,
        bar: {
            ...oldValue?.foo?.bar,
            alice: newAlice
        },
    },
};

Solution: write hooks using immer.js (or use community version).

const [state, setState] = useImmerState({foo: {bar: 1}});
setState(s => s.foo.bar++);
setState({foo: {bar: 2}});

const [state, dispatch] = useImmerReducer(
    (state, action) => {
        case 'ADD':
            state.foo.bar += action.payload;
        case 'SUBTRACT':
            state.foo.bar -= action.payload;
        default:
            return;
    },
    {foo: {bar: 1}}
);

dispatch('ADD', {payload: 2});

Encapsulation of state and behavior

Most development of components and feature implements belongs to the pattern "one state + a serious of behavior".
The state and the behaviors are strongly related.
This pattern is similar to class concept in OO.

In hooks, we write somehow like this:

const [name, setName] = useState('');
const [age, SetAge] = useState(0);
const birthday = useCallback(
    () => {
        setAge(age => age + 1);
    },
    [age]
);

Problems:

  1. Repeated useState and useCallback is bad for code reuse.
  2. Hard to find the relationship between behavior and properties.

Solution: useMethods is an encapsulation of one state and behaviors related to this state.

const userMethods = {
    birthday(user) {
        user.age++; // with immer.js
    },
};

const [user, methods, setUser] = useMethods(
    userMethods,
    {name: '', age: 0}
);

methods.birthday();

Abstract of data structure

Problem:

  1. Some data structure's immutable manipulation is complex, like Array.splice.
  2. Semantic changes. For example, setState doesn't return a value, while Array.pop returns the popped element.
  3. Some types like Set and Map are always mutable.

Solution: lots of hooks in community like useNumber, useArray, useSet, useMap, useBoolean, useToggle.

// A implement of useArray
const [list, methods, setList] = useArray([]);

interface ArrayMethods<T> {
    push(item: T): void;
    unshift(item: T): void;
    pop(): void;
    shift(): void;
    slice(start?: number, end?: number): void;
    splice(index: number, count: number, ...items: T[]): void;
    remove(item: T): void;
    removeAt(index: number): void;
    insertAt(index: number, item: T): void;
    concat(item: T | T[]): void;
    replace(from: T, to: T): void;
    replaceAll(from: T, to: T): void;
    replaceAt(index: number, item: T): void;
    filter(predicate: (item: T, index: number) => boolean): void;
    union(array: T[]): void;
    intersect(array: T[]): void;
    difference(array: T[]): void;
    reverse(): void;
    sort(compare?: (x: T, y: T) => number): void;
    clear(): void;
}

Ecaplutaion of general scene

For example

These ecaplutations should not coupled with UI components.
They are able to apply on different UI components/lib.

TBD...

Top comments (0)