We face two problems when we want to replace a class component with a functional component.

  1. Function components have no state
  2. Function components have no life cycle

React V16.8.0 provides the Hooks API to address these issues.

useState

  • Using a state
const [n,setN] = React.useState(0)
const [user,setUser] = React.useState({name:'Chili'})
Copy the code

To refer to a state, the first parameter is used to read and the second parameter is used to write.

For example, click the button to add one

function App() { const [n, setN] = React.useState(0); Console. log(' execute once ') return (<div className="App"> n: {n} <button onClick={() => setN(n +1)}>+1</button> </div>); }Copy the code

Log finds that every time you click the button to execute setN(n + 1), App() will be reexecuted, and the value of n is different every time you run useState(0), so will executing setN change n? Won’t! May feel strange, Kangkang the following principle:

UseState principle analysis

  • SetN triggers App to rerender render
  • SetN doesn’t change n, it modifies x, putting n+1 into X
  • UseState reads the latest value of n from x
  • In addition, old n and new N are different objects, which reflects the function idea of React
  • Each component has its own data x, which we’ll name state
  • So setN puts the new value into a store, and useState goes to the store to get the new value. And update the data.

Try implementing: see here

const rootElement = document.getElementById("root"); let _state; Function myUseState(initialValue) {_state = _state === = undefined? initialValue : _state; Function setState(newState) {// modify _state _state = newState; render(); } return [_state, setState]; } const render = () => ReactDOM.render(<App />, rootElement); function App() { const [n, setN] = myUseState(0); return ( <<div className="App"> n : {n} <button onClick={() => setN(n + 1)}>+1</button> </div> ); } ReactDOM.render(<App />, rootElement);Copy the code

How to verify that old and new n are different objects? It looks to us like we just changed the content of n. But it’s not what we thought…

New n and old n

Here’s an example:

  1. Operation 1: Click +1 before clicking log
  2. Operation 2: Click log and then +1
function App() {
  const [n, setN] = React.useState(0);
  const log = () => setTimeout(() => console.log(`n: ${n}`), 3000);
  return (
    <div className="App">
      <p>{n}</p>
      <p>
        <button onClick={() => setN(n + 1)}>+1</button>
        <button onClick={log}>log</button>
      </p>
    </div>
  );
}
Copy the code

Operation two has a bug? Log out old data

Analysis is as follows:

So you do create new objects.

SetState cannot be locally updated

Can I partially setState when state is an object?

For example: Change the name

function App() {
  const [user,setUser] = useState({name:'Chili', age: 18})
  const onClick = ()=>{
    setUser({
      name: 'Jack'
    })
  }
  return (
    <div className="App">
      <h1>{user.name}</h1>
      <h2>{user.age}</h2>
      <button onClick={onClick}>Click</button>
    </div>
  );
}
Copy the code

Result: Name was changed successfully, but age was not. How to solve it? The use of… The operator

setUser({ ... user, name: "Jack" });Copy the code
  • SetState is not going to help us merge properties. Can be used… To solve
  • . The user first gives the contents of the old object to the new object
  • Changes are made based on the contents of the new object

Is there another way to write it?

Object address to change

When the name is changed with the following code:

    user.name = "Jack";
    setUser(user);
Copy the code

A bug occurred and the change failed

  • In setState(obj), if the obj address does not change, React assumes that the data has not changed and will not render.
  • Even if the contents of the object are modified

UseState receiver function

Since the initial value is only used by useState on the first render, the JS engine parses the object for each subsequent render. But the JS engine does not parse functions. So the advantage of writing the initial value as a function is to reduce the unnecessary calculation process.

const [user, setUser] = useState(() => {
    return { name: "Chili", age: 18 };
  });
Copy the code

This function returns the initial value state and is executed only once.

SetState accepts the function

For example: consecutive increments of one

function App() { const [n, setN] = useState(0); const onClick = () => { setN(n + 1); setN(n + 1); / / you will find that the n cannot add 2 / / setN (I = > I + 1) / / setN (I = > I + 1)}; return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>+2</button> </div> ); }Copy the code
  • Multiple operations on state should use setN(I => I +1)
  • It doesn’t tell us the value of n. Instead, you pass an operation and return a result

useContext

We learned from useSate that we can have multiple n’s by setN(n+1). What if I want to have a state that runs through? UseContext can be used not only throughout, but also across different components

  • context
  • A global variable is a global context
  • Context is a local global variable

Method of use

  • Const C = createContext(initial)
  • Scope delineation: < C.provider >
  • Use useContext(C) in scope to useContext
  • Assigns a value to a global variable, initializes an object for use by children, who can also make changes. But change is not reactive. It’s a top-down notification process.

Here’s an example:

const C = createContext(null); Function App() {console.log("App executed "); const [n, setN] = useState(0); Return (< c.provider value={{n, setN}}> // value is an object <div className="App"> <Baba /> </div> </ c.provider >); } function Baba() { const { n, setN } = useContext(C); Return (<div> I am father n: {n} <Child /></div>); } function Child() { const { n, setN } = useContext(C); const onClick = () => {setN(i => i + 1); }; <button onClick={onClick}>+1</button> </div>); }Copy the code

Click the button n all changes

useRef

You can also use useRef if you want to have a state throughout. UseRef can be used not only for divs, but also for arbitrary data

  • Purpose: Requires a value that remains constant as the component is rendered
  • Initialization: const count = useRef(0)
  • Read: count. The current
function App() { const nRef = React.useRef(0); const log = () => setTimeout(() => console.log(`n: ${nRef.current}`), 1000); Return (<div className="App"> <p>{ref.current}) </p> <p> <button onClick={() => (ref.current +=) 1)}>+1</button> <button onClick={log}>log</button> </p> </div> ); }Copy the code

Clicking on the button doesn’t update in real time, but the log is correct. What’s going on?

Cannot automatically render when changing

  • Because React doesn’t fit the functional concept
  • So to render automatically you have to listen to the ref yourself
  • When ref.current changes, setN is called

forwardRef

A Class component can accept a REF, but a function component cannot accept a ref directly from someone else. Just use the forwardRef.

  • Because props could not pass the ref attribute
  • Use forword to achieve the transfer of ref

For example, when we use useRef to reference a component

function App() { const buttonRef = useRef(null); <div className="App"> <Button2 ref={buttonRef}> button </Button2> </div>; } const Button2 = props => { return <button className="red" {... props}/>; }; const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);Copy the code

How to solve the console error? Wrap the component with the forwardRef()

const Button3 = React.forwardRef((props, ref) => {xianxing return <button className="red" ref={ref} {... props}/>; });Copy the code

useReducer

Practice Flux/Redux. Write operations on data into functions

  • Create the initial value initialState
  • Reducer (state,action) The first reducer(state,action) function is the data state and the second reducer(action) function is the operation type and returns the data result of the operation
  • Pass the operation and initial state to the useReducer to get the read and write API
  • Call use ({type:’ operation type ‘})

For example, click a button to perform different operations on n

Const initial = {// initialize n: 0}; If (action.type === "add") {return {n: state.n + action.number}; } else if (action.type === "multi") { return { n: state.n * 2 }; } else { throw new Error("unknown type"); }}; function App() { const [state, dispatch] = useReducer(reducer, initial); Const {n} = state; const onClick = () => {dispatch({ type: "add", number: 1 }); }; // call const onClick2 = () => {dispatch({type: "multi"}); }; return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>+1</button> <button onClick={onClick2}>+2</button> </div> ); }Copy the code

Instead of a story

Use useReducer and useContext

  • Put the data in a Store object
  • Put the operations on the data into the Reducer function
  • Create a Contect context
  • Create a useReducer to get the read and write API

An example is to implement a display of user information

useEffect

Since function components do not have the lifecycle functions that Class components do, we use useEffect to simulate the lifecycle functions

use

  • Used as componentDidMount, [] is the second argument

Render only the first time, because it’s an empty array

UseEffect (() = > {the console. The log (' first render ')}, []) / / [] the variables inside again after the changeCopy the code
  • Used as componentDidUpdate to specify dependencies

Note: Render includes the first

UseEffect (() = > {the console. The log (' execute arbitrary properties change ')}) useEffect (() = > {the console. The log (' n changes performed ')}, [n])Copy the code
  • Used as componentWillUnmount, return returns

Returns a function to cancel the timer before the component dies

UseEffect (()=>{const id = setInterval(()=>{console.log(' component will die ')},1000) return ()=>{window.clearInterval(id)}})Copy the code

Side effects (alias for useEffect)

In addition to useEffect, there is also useLayoutEffect side effect API

  • Changes to the environment are side effects

For example: use the useEffect and use the useEffect respectively

UseEffect const App = () => {const [value, setValue] = useState(0); useEffect(() => { document.querySelector('#x').innerText = `value: 1000` }, [value]); return ( <div id="x" onClick={() => setValue(0)}>value: {value}</div> ); };Copy the code

At first, the screen value shows 0 and then suddenly becomes 1000, another flashing process.

// 使用 useLayoutEffect
const App = () => {
  const [value, setValue] = useState(0);
  useEffect(() => {
    document.querySelector('#x').innerText = `value: 1000`
  }, [value]);

  return (
    <div id="x" onClick={() => setValue(0)}>value: {value}</div>
  );
};
Copy the code

There is no flashing process and the value is displayed as 1000.

Analysis of the

  • UseEffect is executed after the browser has rendered as well as after render
  • UseLayoutEffect is executed before the browser renders

UseLayoutEffect characteristics

  • Layout side effects: Used to change the appearance of the screen
  • UseLayoutEffect is executed before useEffect
  • Tasks in useLayoutEffect should affect Layout
  • Since most scenes cannot change the DOM directly, useEffect should be used first to achieve a better user experience

useMemo

Before studying useMemo, you should know the Memo first

memo

Here’s an example:

function App() { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); const onClick = () => {setN(n + 1); }; return ( <div className="App"> <button onClick={onClick}>update n {n}</button> <Child data={m} /> </div> ); } function Child(props) {console.log(" Child performed "); return <div>child: {props.data}</div>; }Copy the code

So every time I hit n plus one, I’m going to execute the log Child, so I’m going to execute the child every time.

  • React defaults to redundant render
  • If the props are unchanged, there is no need to execute a function component again
  • Mome can save additional operations: Child2 = react.Memo (Child) instead of Child

Is there a bug when props passes functions? And then there’s the extra render

  • Each render generates a new function.
  • Although the old and new functions have different functions, they have different addresses because they are different objects.
  • React expects the props to change and then execute Child2.
  • Why doesn’t this happen when n changes?
  • N is a number. This is the difference between a base type and a reference type

How to solve it? Using useMemo

UseMemo characteristics

  • Implement function reuse
  • Parameters are defined
  • Calculates new dependency values based on changes in dependencies
  • If the dependency does not change, the previous dependency value is used
  • Computed equivalent to Vue 2

For example, observe the log results of clicking different buttons

function App() { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); const onClick = () => { setN(n + 1); }; const onClick2 = () => { setM(m + 1); }; const onClickChild = useMemo(() => { const fn = (div) => { console.log("on click child, m: " + m); }; return fn; }, [m]); Return (<div className="App"> <button onClick={onClick}>update n {n}</button> <button onClick={onClick2}>update m {m}</button> <Child2 data={m} onClick={onClickChild} /> </div> ); } function Child(props) {console.log(" Child performed "); return ( <div onClick={(e) => props.onClick(e.target)}> <button>child: {props.data}</button> </div> ); } const Child2 = React.memo(Child);Copy the code

Find that the first argument is a function that returns a function. This can be simplified using useCallback

useCallback

Syntax sugar similar to useMemo

- useCallback (x = > log (x), [m]) is equivalent to useMemo (() = = > > x log (x), [m])Copy the code

Customize the Hook

  • Write the related operations in a function
  • Only read and write interfaces are exposed

The sample

conclusion

  • UseState state
  • UseContext context
  • UseRef reference forwardRef
  • UseReducer complex useState
  • UseEffet side effects useLayoutEffect
  • UseMemo memory callback useCallback

Add: Stale Closure

Obsolete closures: react-rex-stale-closures

  • Problem: A function references a variable that was previously created
  • Resolution: Create dependencies