前言
如果对
re-render
概念还不清楚,建议先看 React & 理解 re-render 的作用、概念,并提供详细的例子解释 再回头看本文。
如果对 React 基础语法还不熟练,建议先看 React & JSX 日常用法与基本原则 再回头看本文。
useState
useState
可以用来声明响应式数据。
使用案例:
js
import ReactDOM from 'react-dom/client';
import { useState } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const App = () => {
const [name, setName] = useState('Jack')
return <>
<div>My name is {name}</div>
<button onClick={() => setName('John')}>Change my name</button>
</>
}
root.render(
<App />
);
效果:
注意,state 更新采用异步执行 re-render,因此控制台打印的值仍然是旧的。
若希望能够在 state 变更后马上拿到最新的值,有两种方案:
- 使用临时变量存储要变更的值:
js
// ...
const onChangeName = () => {
const tempName = 'John'
setName(tempName)
console.log('My current name is', tempName)
}
// ...
- 使用 useRef,下面会讲。
useRef
useRef
也是声明响应式数据的一种方式,与 useState 不同的是,它可以不受 re-render 的约束,在更新数据后能立刻访问最新的值。
使用案例:
js
import ReactDOM from 'react-dom/client';
import { useRef } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const App = () => {
const name = useRef('Jack')
const onChangeName = () => {
name.current = 'John'
console.log('My current name is', name.current)
}
return <>
<div>My name is {name.current}</div>
<button onClick={onChangeName}>Change my name</button>
</>
}
root.render(
<App />
);
效果:
可以看出 useRef
与 useState
是两种相反的结果,控制台为最新,视图为旧。
在不强调视图的情况可以考虑用 useRef
,比如异步回调获取最新数据的场景。
useReducer
useReducer
是 useState
的进阶版,当 useState 操作逻辑变得复杂时,可以将它们提升为 useReducer
的书写方式 ,提高可读、维护性。
下面是 useEffect VS useReducer 相同功能的对比图:
useReducer
通过 switch
声明式结构,我们能一眼就能看出每个 type 都做了哪些事。
从对比图看,虽然没能展现 useReducer
的优势,但我们只要理解它的逻辑处理结构就行了,
多一种选择,何乐而不为呢。
在 useState 处理少量逻辑的情况下优先 useState
,反之 useReducer
。
案例代码:
js
import ReactDOM from 'react-dom/client';
import { useReducer } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const todoReducer = (state, action) => {
switch (action.type) {
case 'add':
return {
...state,
todos: [
...state.todos,
Math.floor(Math.random() * 1000)
]
}
case 'remove':
return {
...state,
todos: state.todos.splice(state.index)
}
default:
return state
}
}
const App = () => {
const [state, dispatch] = useReducer(todoReducer, { todos: [] })
return <>
<header>Todos</header>
<button onClick={() => dispatch({ type: 'add' })}>Add todo</button>
<button onClick={() => dispatch({ type: 'remove' })}>Remove todo</button>
<ul>
{state.todos.map((todo) => (
<li key={todo}>
{todo}
</li>
))}
</ul>
</>
}
root.render(
<App />
);
参数解释:useReducer(reducer, initialArg, init?)
- 第一个参数是一个 handler,即要处理数据的函数。
- 第二个参数是默认值,和 useState(xx) 的第一个参数一样。
- 第三个参数是一个 useMemo 回调函数(可选项),它可以缓存第二个参数的数据,避免 re-render 重复定义默认值。
useReducer 返回的 state
/ dispatch
表示:
- state 获取我们的数据。
- dispatch 触发 handler 函数
提示:掌握了
useReducer
等于学会了react-redux
框架,它与 useReducer 的区别仅仅多了一层全局缓存的含义,对 react-redux 感兴趣的可参考:
React & 用一个简单案例体验一遍 React-dom & React-router & React-redux 全家桶
useContext
useContext
可以解决多层组件传递 props 数据的问题,如果你用过 Vue 的 provide/inject 函数,那你也就会了,它们俩的作用&概念是一致的。
案例代码:
js
import ReactDOM from 'react-dom/client';
import { createContext, useContext, useState } from 'react';
const root = ReactDOM.createRoot(document.getElementById("root"));
const Profile = () => {
const userInfo = useContext(UserInfoContext)
return <>
<label>Name: {userInfo.name}</label><br />
<label>Age: {userInfo.age}</label><br />
<label>Hobbies:
{userInfo.hobbies.map(hobby => <span key={hobby}>{hobby} </span>)}
</label>
</>
}
const ShoppingCart = () => {
const userInfo = useContext(UserInfoContext)
return <>
<footer>
Shopping Cart:
</footer>
<ul>
{userInfo.carts.map((cart) => <li key={cart}>{cart}</li>)}
</ul>
Total num: {userInfo.carts.length }
</>
}
const UserInfoContext = createContext(null)
const App = () => {
const [userInfo] = useState({
name: 'Jack',
age: 30,
hobbies: ['Running', 'Painting'],
carts: ['Dog toy', 'Cup']
})
return <>
<UserInfoContext.Provider value={userInfo}>
<Profile/>
<ShoppingCart/>
</UserInfoContext.Provider>
</>
}
root.render(
<App />
);
效果:
useMemo / useCallback
useMemo
可以缓存非响应式数据,避免 re-render
的问题。
useCallback
可以缓存函数,避免 re-render
的问题。
对于函数缓存,虽然 useMemo 也能实现,但还是得额外嵌套一层函数,因此官方建议使用
useCallback
。
俩钩子的用法在 React & 理解 re-render 的作用、概念,并提供详细的例子解释 都有详细的解释,这里不再赘述。
完!