react初学者对于 hooks 的理解
useState
让我们函数组件中也拥有 satate,这样我们就可以永远不用 class 组件啦
import React, { useState } from 'react';
function Example() {
// 声明一个叫 "count" 的 state 变量
const [count, setCount] = useState(0);
// 第一个参数是你的数据
// 第二个参数是更新数据的方法
// 右侧useState()中的参数是你的数据的默认数据,可以是复杂对象
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}> // 使用 setCount 更新数据
Click me
</button>
</div>
);
}
- 如果 state 是一个复杂对象,能否部分更新 setState?
不能,你可以使用...
拓展运算符把之前的数据混入
function Example (){
const [state, setState] = useState({
name:'bibi',
age:18
});
return (
<div>
<p>我的名字是{state.name}</p>
<p>我今年{state. age}岁了</p>
<button onClick={() => setState({...state,name:'fineley'})}>
改名
</button>
</div>
);
}
- 如果setState(obj)的内存地址没有变化,那么 React 就认为数据没有变化
function Example () {
const [state, setState] = useState({
name:'bibi',
age:18
});
const onClick = () => {
sate.name = 'fineley'
setState(state) // 无效!!! 因为你的地址没有变化,请生成一个新的对象
}
return (
<div>
<p>我的名字是{state.name}</p>
<p>我今年{state. age}岁了</p>
<button onClick={onClick}>
改名
</button>
</div>
);
}
-
useState 的第一个参数可以是一个函数
function Example (){ const [state, setState] = useState(()=>({ name:'bibi', age:9+9 })); // ... }
有什么区别呢?
可以生成一些复杂的对象,在非初次执行时会少计算一次数据(没啥卵用)
-
setState 可以接受函数
import React, { useState } from 'react'; function Example() { const [count, setCount] = useState(0);] const onClick = ()=>{ setCount(n+1) setCount(n+1) // 只会执行会后一次 // 正确使用 setCount(x=>x+1) // x 是回调的占位符可以是任何参数 setCount(x=>x+1) // 将会按预期执行 } return ( <div> <p>You clicked {count} times</p> <button onClick={onClick}> // 使用 setCount 更新数据 Click me </button> </div> ); }
useEffect
Effect Hook 可以让你在函数组件中执行副作用操作,官方文档的描述我一直没看懂,不一定是执行副作用,副作用也不一定在 useEffect中执行
我对于useEffect的理解是叫做 afterRender,每次 render 之后执行
- 作为 componentDidMount 使用,[]作为第二个参数,没有依赖任何参数,用于初始化操作,请求数据等操作
- 不要试图去欺骗 react.请把所有的依赖卸载第二个参数中,否则会导致意想不到的结果
- 每次渲染都执行执行
- 只在依赖变化的时候执行(有点类似 vue 的 watch)
- 当组件卸载时候 return 一个 fn 里面写上执行的操作
- 使用多个useEffect顺序是从上到下执行
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
const [name, setName] = useState('bibi');
useEffect(() => {
console.log('我出生了')
},[]); //1. 只执行一次(空数组里面的变量变化时执行)
useEffect(() => {
console.log('我出生了123456')
}); //2. 每次都执行(任何一个状态变化都会执行)
useEffect(() => {
console.log('name 变了我就变')
},[name]); //3. 第一此和依赖变化都会调用
return (
<div>
<p>{name}</P>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
// ... fn changename
</div>
);
}
useLayoutEffect
-
useLayoutEffect会在 dom 生成完成之后,render 之前调用,而useEffect 会在 render 之后调用(useLayoutEffect在浏览器渲染之前执行,useEffect 在浏览器渲染之后执行)
-
当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制
- useLayoutEffect 总是比 useEffect 先执行
- 使用useLayoutEffect中最好改变了 layout
- 优先使用 useEffect(优先渲染)
useReducer
useReaducer 是 useState 的升级版,含有复杂逻辑可以适用 useReaducer,用于减少复杂度
const initialState = {count: 0}; // 创建初始值
const reducer = (state, action)=> { // state:之前的数据,action:动作
switch (action.type) {
case 'add':
return {count: state.count + action.number};
case 'desc':
return {count: state.count - action.number};
default:
throw new Error('unknown type');
}
}
const Counter() => {
const [state, dispatch] = useReducer(reducer, initialState); // 初始化
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'desc', number: 1})}>-</button>
<button onClick={() => dispatch({type: 'add', number: 1})}>+</button>
</>
);
}
用 useReducer 结合 useContext 实现 Redux
- 将数据集中在一个 store 对象
- 将所有操作集中再 reducer
- 创建一个 context
- 创建对数据的读写 api
- 将第四步的读写 api 放到第三步的 context 中
- 用 context.provider 将 context提供给所有组件
- 各个组件用 usecontext 获取读写 api
useMemo
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算,用于优化性能
-
父元素每次渲染会重新渲染子元素,可以适用 useMemo 让当只有依赖项变化时才重新渲染.用 React.memo 包一层,当 props 不变时,不会重新 render,当 props 里面有函数的时候,函数的地址会发生变化,react看来 props 还是发生了变化,props 中有函数的时候需要把函数包一层 useMemo 才能实现想要的结果
-
用于缓存函数,结合 React.memo 用于优化性能,减少多余的 render
-
useMemo 接受一个函数,函数的返回值就是你需要的缓存的函数/对象等
-
十分像 vue 的 computed(缓存/计算/返回值)
const memoizedValue = useMemo(() => { return ()=>{} },[a]); // 依赖
useCallback
如果你觉得 useMeno 写的很别扭
useCallback(fn, deps)
相当于 useMemo(() => fn, deps)
const memoizedValue = useMemo(()=>{},[a]); // 依赖
useRef
如果你希望有一个值在 render 过程中保持不变(地址),请适用 useref
- 使用了 current 才能保证两次渲染是同一个值(只有引用能做到)
- 在 fn 组件中如果要传递 ref 需要用 React.forwardRef 包一层,作为组件的第二个参数传递
const ref = React.useRef(null)
<FancyButton ref={ref}>Click me!</FancyButton>;
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref}>
{props.children}
</button>
));
useImperativeHandle
- useImperativeHandle可以自定义 ref 传递给父组件使用
const FancyInput = (props, ref)=>{
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} ... />;
}
FancyInput = forwardRef(FancyInput);
自定义 hook
- 用于将组件逻辑提取到可重用的函数中