Try to write more advanced React
Tags: react
Published: 2020-06-05 20:06:06

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.

```js
const newValue = {
    ...oldValue,
    foo: {
        ...oldValue?.foo,
        bar: {
            ...oldValue?.foo?.bar,
            alice: newAlice
        },
    },
};
```
**Solution**: write hooks using immer.js (or use community version).

```js
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:
```js
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`](https://github.com/pelotom/use-methods/blob/master/README.md) is an encapsulation of one state and behaviors related to this state.
```js
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`.

```ts
// 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
- [Alibaba's umi.js](https://hooks.umijs.org/hooks/async)

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

## TBD...