useState

  • Using a state

    const [n, setN] = React.useState(0)
    const [user, setUser] = React.useState({name: 'bbter', age: 18})
    Copy the code
  • Note 1: Cannot be partially updated

    If state is an object, can I partially setState?

    And the answer is no, because setState won’t help us merge properties

    The previous object needs to be passed manually, as shown in the figure below

    Does useReducer merge attributes? Don’t!

    React thinks it’s your job to do it yourself

  • Note 2: Change the address

SetState (obj) If the obj address does not change, React assumes that the data has not changed and will not update the view

  • UseState receiver function
Const [state, setState] = useState(() => {return initialState}) // this function returns the initialState and is executed only onceCopy the code

  • SetState accepts the function
    SetN (I => I + 1) If you can accept this form, use this form firstCopy the code

useReducer

  • Practice Flux/Redux

    Look at the code. Four steps

    Create an initial value initialState

    Reducer (state, action);

    3. Pass to userReducer to obtain read and write apis

    Call write ({type: ‘operation type ‘})

    In general, useReducer is a sophisticated version of useState.

Use useReducer instead of redux

Put the data in a store object

2. Focus all operations on the Reducer

Create a Context

Create an API for reading data

5. Place the contents of step 4 into the Context of step 3

Use context. Provider to provide Context to all components

7. Each component uses useContext to get the read and write API

import React, { useReducer, useContext, useEffect } from "react"; import ReactDOM from "react-dom"; const store = { user: null, books: null, movies: null }; function reducer(state, action) { switch (action.type) { case "setUser": return { ... state, user: action.user }; case "setBooks": return { ... state, books: action.books }; case "setMovies": return { ... state, movies: action.movies }; default: throw new Error(); } } const Context = React.createContext(null); function App() { const [state, dispatch] = useReducer(reducer, store); const api = { state, dispatch }; return ( <Context.Provider value={api}> <User /> <hr /> <Books /> <Movies /> </Context.Provider> ); } function User() { const { state, dispatch } = useContext(Context); useEffect(() => { ajax("/user").then(user => { dispatch({ type: "setUser", user: user }); }); } []); Return (<div> <h1> Personal information </h1> <div>name: {state.user? state.user.name : ""}</div> </div> ); } function Books() { const { state, dispatch } = useContext(Context); useEffect(() => { ajax("/books").then(books => { dispatch({ type: "setBooks", books: books }); }); } []); Return (<div> <h1> my books </h1> <ol> {state.books? State. Books. The map (book = > < li key = {book. Id} > {book. The name} < / li >) : "load"} < / ol > < / div >). } function Movies() { const { state, dispatch } = useContext(Context); useEffect(() => { ajax("/movies").then(movies => { dispatch({ type: "setMovies", movies: movies }); }); } []); Return (<div> <h1> my movies </h1> <ol> {state.movies? State. Movies. The map (movie = > < li key = {movie. Id} > {movie. The name} < / li >) : "load"} < / ol > < / div >). } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement); // Help function // false ajax // After two seconds, returns an object according to path, Function ajax(path) {return new Promise(resolve, reject) => { setTimeout(() => { if (path === "/user") { resolve({ id: 1, name: "Frank" }); } else if (path === "/books") {resolve([id: 1, name: "JavaScript advanced programming "}, {id: 2, name: "JavaScript advanced programming "}]); } else if (path = = = "/ movies") {resolve ([{id: 1, name: "before sunrise"}, {id: 2, name: "notebook"})); }}, 2000); }); }Copy the code

useContext

  • context

A global variable is a global context, and a context is a local global variable

  • Method of use

    CreateContext with C = createContext(initial

    2. Use <C. provider > to define the scope

    Use useContext(C) in the scope to use the context

  • Matters needing attention

    It’s not reactive. You change the value of C in one module, and the other module doesn’t notice the change

useEffect

  • Side effects

    Changes to the environment are side effects, such as modifying document.title

    But we don’t have to put side effects in useEffect

    It’s actually better called afterRender, executed after each render

  • use

    For componentDidMount, [] is the second parameter

    B, as componentDidUpdate use, can specify a dependency

    Use as componentWillUnmount, via return

    Four, the above three uses can exist simultaneously

  • The characteristics of

    If multiple USEeffects exist at the same time, they are executed in the order in which they occur

useLayoutEffect

  • Layout side effect

    UseEffect is executed after the browser rendering is complete

    UseLayoutEffect is executed before the browser renders

  • The characteristics of

    UseLayoutEffect is always executed before useEffect

    Tasks in useLayoutEffect best affect Layout

  • experience

    UseEffect is preferred for user experience

function App1() { const [n, setN] = useState(0) const time = useRef(null) const onClick = () => { setN(i => i + 1) time.current = performance.now() } useLayoutEffect(() => {if (time.current) {console.log(performance.now() - time.current) // => {if (time.current) {console.log(performance.now() - time.current) // about 2.7ms}}) return (<div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>Click</button> </div> ); }Copy the code

/* useLayoutEffect */ function App2() {const [n, SetN] = useState(0) const onClick => {setN(I => I + 1)} useEffect(() => {console.log(1)}) useLayoutEffect(() => { console.log(2) }) useLayoutEffect(() => { console.log(3) }) return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>Click</button> </div> ); }Copy the code

useMemo

  • To understand the React. UseMemo

    You need to talk about React. Memo first

    React defaults to redundant render

function App() { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); const onClick = () => { setN(n + 1); }; <button onClick={onClick}>update n {n}</button> </div> <Child data={m}/> {/* <Child2 data={m}/> */} </div> ); } function Child(props) {console.log(" Child performed "); Console. log(' assuming there's a lot of code here ') return <div>child: {props. Data}</div>; } const Child2 = React.memo(Child);Copy the code

Replace the Child in the code with react.Memo (Child)

If the props are unchanged, there is no need to execute a function component again

Final code:

function App() { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); const onClick = () => { setN(n + 1); }; <button onClick={onClick}>update n {n}</button> </div> <Child data={m}/> </div> ); } const Child = React. Memo (props => {console.log(" Child executed "); Console. log(' assuming there's a lot of code here ') return <div>child: {props. Data}</div>; });Copy the code

But there’s a bug with this thing

After adding the listener function, it breaks in a second because when the App runs, onClickChild will be executed again to generate a new function

The old and new functions function the same, but address reference is different!

function App() { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); const onClick = () => { setN(n + 1); }; Const onClickChild = () => {} return (<div className="App"> <div> {/* Clicking button re-executes Child component */} <button OnClick ={onClick}>update n {n}</button> </div> {/* However, if a reference is passed, the react. memo is invalid. Because references are unequal */} <Child data={m} onClick={onClickChild}/> </div>); } const Child = React. Memo (props => {console.log(" Child executed "); Console. log(' assuming a lot of code here ') return <div onClick={props. OnClick}> Child: {props. Data}</div>; });Copy the code

How to do? Use useMemo:

function App() { const [n, setN] = React.useState(0); const [m, setM] = React.useState(0); const onClick = () => { setN(n + 1); }; const onClick1 = () => { setM(m + 1); }; const onClickChild = () => {} const onClickChild1 = useMemo(() => { return () => { console.log(`on click child m: ${m}`) } }, <button onClick={onClick}>update n {n}</button> <button onClick={onClick1}>update m {m}</button> </div> {/* */} {/*<Child data={m} onClick={onClickChild}/>*/} {/*onClickChild1 useMemo can eliminate this bug*/} <Child data={m} onClick={onClickChild1}/> </div> ); } // Const Child = React. Memo (props => {console.log(" Child executed "); Console. log(' assuming a lot of code here ') return <div onClick={props. OnClick}> Child: {props. Data}</div>; });Copy the code
  • role

It is used to cache some value that you want to use in both iterations of the old and new components

  • The characteristics of

The first argument is () => value

The second argument is dependent on [m, n]

A new value is computed only when the dependency changes

If the dependency does not change, reuse the previous value

Isn’t this computed in Vue 2?

useCallback

  • Pay attention to

If your value is a function, you would write useMemo(() => x => console.log(x)).

This is a function that returns a function

Is it difficult to use? Hence the useCallback

  • usage

UseCallback (x => console.log(x), [m]) is equivalent to

useMemo( () => x => console.log(x), [m])

forwardRef

  • useRef

Can be used to reference DOM objects

It can also be used to refer to ordinary objects

  • forwardRef

Props could not pass the ref attribute

Function App(){const buttonRef = useRef(null) return (<div> <Button ref={buttonRef}> Button </Button> {/* Console error: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?  */} </div>)} const Button = (props) => {console.log(props) // {ref: undefined, children: "Button "} return < Button {... props} /> }Copy the code

Implement the transfer of ref: because the props does not contain ref, the forwardRef is required

import React, {forwardRef, useRef} from 'react'; Function App(){const buttonRef = useRef(null) return (<div> <Button ref={buttonRef}> Button </Button2> </div>)} const Button = function (props, ref) => {console.log(ref) Return <button ref={ref} {... props} />; })Copy the code

Customize the Hook

  • Encapsulating data operation

A simple example

// useList.js
import {useState, useEffect} from 'react'

const useList = () => {
    const [list, setList] = useState(null)
    useEffect(() => {
        ajax().then(list => {
            setList(list)
        })
    }, []) //确保只在第一次运行
    return {
        list,
        setList
    }
}
export default useList

function ajax(){
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve([
                {id: 1, name: 'Frank'},
                {id: 2, name: 'Jack'},
                {id: 3, name: 'Alice'},
                {id: 4, name: 'Bob'},
                {id: 5, name: 'Han'}
            ])
        }, 1000)
    })
}

//index.js
import useList from './hooks/useList'

function App(){
    const {list, setList} = useList()
    return (
        <div>
            <h1>List</h1>
            {
                list ? (
                    <ol>
                        {
                            list.map(item => {
                                return <li key={item.id}>{item.name}</li>
                            })
                        }
                    </ol>
                ):(
                    '加载中...'
                )
            }
        </div>
    )
}

Copy the code

Close case

// useList.js import {useState, useEffect} from 'react' const useList = () => { const [list, SetList] = useState(null) useEffect(() => {ajax().then(list => {setList(list)})}, []) addItem: name => { setList([...list, {id: Math.random(), name}]) }, deleteIndex: index => { setList(list.slice(0, index).concat(list.slice(index + 1))) } } } export default useList function ajax(){ return new Promise((resolve, reject) => { setTimeout(() => { resolve([ {id: 1, name: 'Frank'}, {id: 2, name: 'Jack'}, {id: 3, name: 'Alice'}, {id: 4, name: 'Bob'}, {id: 5, name: 'Han'} ]) }, 1000) }) } //index.js import useList from './hooks/useList' function App() { const {list, deleteIndex} = useList() return ( <div> <h1>List</h1> { list ? ( <ol> { list.map((item,index) => { return ( <li key={item.id}> {item.name} <button onClick={() => { deleteIndex(index);  }} > x </button> </li>)})} </ol>) : ' ) } </div> ) }Copy the code

You can also use Context in custom hooks

UseState only says it can’t run inside an if, not a function, as long as the function is run inside a function component

Custom hooks are a complete substitute for Redux