一、useState
- 函数组件的setState,不能局部更新
- setState(obj),如果obj地址没变那么React不会做更新
- useState可以写成函数和直接写数据没有区别
incrementCount() {
this.setState({count: this.state.count + 1});
}
handleSomething() {
this.incrementCount();
this.incrementCount();
this.incrementCount();
}不要指望在调用 setState 之后,this.state 会立即映射为新的值。incrementCount()会等到handleSomething()执行完之后再去执行,此时setState是异步的
incrementCount() {
this.setState((state) => {
return {count: state.count + 1}
});
}
handleSomething() {
this.incrementCount();
this.incrementCount();
this.incrementCount();
}如果传的是一个函数那么React会依次调用
二、useReducer
const [state, dispatch] = useReducer(reducer, initialArg, init);useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}优点就在于把数据和配套的方法都聚拢在一起
接受参数:
dispatch({type: 'decrement',data:{date1:xxx}}) case 'increment':
return {count: state.count + 1 ,...action.data};三、useContext
- const xxx = createContext(initialValue)
- 使用xxx.Provider圈住范围
<xxx.Provider value={xxx}>
<Toolbar />
</xxx.Provider>- const xxx = useContext(xxx);
四、插播-React的更新机制
React的更新不是响应式的,比如说我们setN之后,页面不会响应式的更新,而是通知组件重新render,来对比改变了什么。
同理Context也不是响应式的,兄弟组件之间修改context,会通知父组件重新渲染,另一个兄弟组件的值才会更新
五、useEffect
本质上是setN代码触发的re-render中再次调用了useEffect,根据第二个参数判断这次re-render结束后是否执行回调。
依然是根据顺序来决定数组(state数据)中的哪一项
useEffect(()=>{
return()=>{
//在这里清除无用代码,防止内存泄漏,相当于destoryed
}
})存在多个钩子按顺序执行
六、useLayoutEffect
- 其函数签名与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。在浏览器执行绘制之前,useLayoutEffect 内部的更新计划将被同步刷新。
- useEffect 是在在浏览器执行绘制之后执行。
- 它会在浏览器执行绘制之前调用 effect
- 尽可能使用标准的 useEffect 以避免阻塞视觉更新,用户就算看到更新过程也算看到了页面,用了layout可能让用户推迟几秒看到页面
- useLayoutEffect先于useEffect
- useLayoutEffect最好和操作dom有关
七、React.memo和useMemo
和类组件的pureCompontent类似,只不过只适用于函数组件,而不适用 class 组件。只要props不变就不重新执行函数组件。
但是添加监听函数后会出现bug,因为重新渲染App组件后,里面的函数重新生成,导致函数的地址改变了,使用相当于props变了。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。如果没有提供依赖项数组,useMemo 在每次渲染时都会计算新的值。
相当于computed。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
八、useRef
const count = useRef(0)
count.current 来读取
为什么需要.current因为,如果count只是一个普通值,那么复制的是值,对象的话复制的是值
改变count.current 并不会刷新UI,配合setN来刷新UI
九、Ref和forwardRef
类组件的ref
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef(); }
componentDidMount() {
this.textInput.current.focusTextInput(); }
render() {
return (
<CustomTextInput ref={this.textInput} /> );
}
}默认情况下,你不能在函数组件上使用 ref 属性,因为它们没有实例:如果要在函数组件中使用 ref,你可以使用 forwardRef
function CustomTextInput(props) {
// 这里必须声明 textInput,这样 ref 才可以引用它
const textInput = useRef(null);
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}一个函数组件,想要接受ref
<button ref={ref} className="FancyButton">
{props.children}
</button>必须通过forwardRef包装整个组件
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));我们就可以传进来了
<FancyButton ref={ref}>Click me!</FancyButton>;前提是已经声明好ref了,React.createRef 创建一个能够通过 ref 属性附加到 React 元素的ref
const ref = React.useRef();- 我们通过调用
React.createRef创建了一个 React ref 并将其赋值给ref变量。 - 我们通过指定
ref为 JSX 属性,将其向下传递给<FancyButton ref={ref}>。 - React 传递
ref给forwardRef内函数(props, ref) => ...,作为其第二个参数。 - 我们向下转发该
ref参数到<button ref={ref}>,将其指定为 JSX 属性。 - 当 ref 挂载完成,
ref.current将指向<button>DOM 节点。
我们在子组件上写上ref属性时,子组件不认识ref属性,所以需要forwardRef
十、useRef 和 createRef
useRef 仅能用在 FunctionComponent,createRef 仅能用在 ClassComponent。
第一句话是显然的,因为 Hooks 不能用在 ClassComponent。
第二句话的原因是,createRef 并没有 Hooks 的效果,其值会随着 FunctionComponent 重复执行而不断被初始化。
上面的forwardRef是指组件能不能接受ref,而不是普通dom能不能接受ref