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