useState
使用状态
const[n, setN] = React.useState(0)
const[user, setUser] = React.useState({name:'F'})
注意事项1——不可局部更新
如果state是一个对象,能否部分setState?
不可以,示例代码
因为setState不会帮我们合并属性,而且useReducer也不会合并属性
注意事项2——地址不变
setState(obj)如果obj地址不变,那么React就认为数据没有变化
地址不变的示例,name不会改变
useState 接受函数
const [state, setState] = useState(() => ({
return initialState
}))
// 该函数返回初始值 state,且只执行一次
在useState中把初始值写成函数的好处是减少多余的计算过程(函数只有在用到的时候才会执行)
setState 接受函数
setN(i => i + 1)
useReducer
useReducer
用来践行 Flux / Redux 的思想
分四步走——
- 创建初始值
initialState - 创建所有操作
reducer(state, action) - 传给
useReducer,得到读和写API - 调用写
({type:'操作类型})
总得来说,useReducer是useState的复杂版
一个用 useReducer 的表单例子
代替 Redux
步骤
- 将数据集中在一个
store对象 - 将所有操作集中在
reducer - 创建一个
Context - 创建对数据的读写
API - 将第四步的内容放到第三步的
Context - 用
context.Probider将Context提供给所有组件 - 各个组件用
useContext获取读写API
例子
useContext
上下文
全局变量是全局的上下文
上下文是局部的全局变量
使用方法
之前例子已经介绍过
- 使用
C=createContext(initial)创建上下文 - 使用
<C.provider>圈定作用域 - 在作用域内使用
useContext(C)来使用上下文
useContext 注意事项
不是响应式的
在一个模块将C里面的值改变,另一个模块不会感知到这个变化
useEffect & useLayoutEffect
useEffect
副作用
对环境的改变即为副作用,如修改document.title
但不一定非要把副作用放在useEffect里
useEffect会在每次render后运行
用途
作为componentDidMount使用,[]作第二个参数
作为componentDidUpdate使用,可指定依赖
作为componentWillUnmount使用,通过return
以上三种用途可同时存在
特点
如果同时存在多个useEffect,会按照出现次序执行
useLayoutEffect
布局副作用
useEffect在浏览器渲染完成后执行
useLayoutEffect在浏览器渲染前执行
特点
useLayoutEffect总是比useEffect先执行
useLayoutEffect里的任务最好影响了Layout
经验
为了用户体验,优先使用useEffect(优先渲染)
useMemo
React.memo
React 默认有多余的render
代码中的Child用React.memo(Child)代替
如果props不变,就没有必要再次执行一个函数组件
React.memo的bug
上面的示例,由于在Child添加了监听函数,App运行时会再次执行第12行,生成新的函数
也就是说,虽然新旧函数功能一样,但是地址不一样
解决办法:用useMemo
useMemo
特点
第一个参数是()=>value,见定义
第二个参数是依赖[m,n]
只有当依赖变化时,才会计算出新的value
如果依赖不变,那么就重用之前的value
这个功能等同于Vue2的computed
注意
如果value是个函数,那么就要写成useMemo(()=>(x)=>console.log(x))
这是一个返回函数的函数
语法糖useCallback可以简化
useCallback
useCallback(x => log(x), [m])
// 等价于
useMemo(() => x => log(x), [m])
useRef
目的
如果需要一个值,在组件不断render时保持不变
// 初始化
const count = useRef(0)
// 读取
count.current
为什么需要current?
为了保证两次useRef是同一个值(只有在引用能做到)
延伸
// 初始化
const count = ref(0)
// 读取
count.value
不同点:当count.value变化时,Vue3会自动render
useRef能做到变化时自动render吗?
可以看到,对染console.log(count.current)打印出的结果有变化,但是button``update count: 0没有变化。说明useRef不能自动render
不能,不符合 React 理念,React理念是UI=f(data)
如果想要这个功能,可以自己加——
监听ref,当ref.current变化时,调用setX即可
forwardRef
代码示例
代码1:props无法传递ref属性
代码2:实现ref的传递
代码3:两次ref传递得到button的引用
useRef
可以用来引用DOM对象
也可以用来引用普通对象
forwardRef
由于props不包含ref,所以需要forwardRef
为什么props不包含ref?
因为大部分时候不需要
useImperativeHandle
代码示例
分析
用于自定义ref的属性
自定义 Hook
封装数据操作
分析
还可以在自定义Hook里使用Context
useState只说了不能在if里,没说不能在函数里运行,只要这个函数在函数组件里运行即可
Stale Closure
过时闭包
参考文章链接