1、useState
2、useEffect 与 useLayoutEffect
1、区别
1、useEffect 是异步执行,useLayoutEffect是同步执行。
2、useEffect 执行时机是在浏览器完成渲染之后,但是useLayoutEffect是在浏览器渲染之前,与ComponentDidMount等价。
2、总结
- 优先使用 useEffect,因为它是异步执行的,不会阻塞渲染
- 会影响到渲染的操作尽量放到 useLayoutEffect中去,避免出现闪烁问题
- useLayoutEffect和componentDidMount是等价的,会同步调用,阻塞渲染
- 在服务端渲染的时候使用会有一个 warning,因为它可能导致首屏实际内容和服务端渲染出来的内容不一致。
3、useMemo与useCallback、Memo
1、useMemo与useCallback
1、区别
每一次渲染 对应的方法都会执行一次 避免重复渲染
useMemo是useCallback的语法糖。
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)。
主要区别是 React.useMemo 将调用 fn 函数并返回其结果,而 React.useCallback 将返回 fn 函数而不调用它。deps是依赖项,数组格式。2、场景
useCallback 优化针对于子组件的渲染(一般子组件使用 memo() 函数)
父组件传递一个函数给子组件的时候,由于父组件的更新会导致该函数重新生成从而传递给子组件的函数引用发生了变化,这就会导致子组件也会更新,而很多时候子组件的更新是没必要的,所以我们可以通过useCallback来缓存该函数,然后传递给子组件。** useMemo 优化针对于当前组件高开销的计算
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。2、React.Memo
1、作用
React 中当组件的 props 或 state 变化时,会重新渲染,实际开发会遇到不必要的渲染场景。当处理一个异步函数时,他的返回依然时promise对象。所有这样处理没有意义2、场景
React.Memo将组件作为函数(memo)的参数,函数的返回值(Child)是一个新的组件。4、useContext
组件共享状态-子孙组件 ``` import React,{useContext} from ‘react’; //创建context const numberContext = React.createContext(); //它返回一个具有两个值的对象 //{Provider , Consumer} function App(){ //使用Provider为所有子孙提供value值 return ( ) }<div>
<ShowAn />
</div>
function ShowAn(){
//使用Consumer从上下文获取value
//调用useContext,传入从React.createContext获取的上下文对象。
const value = useContext(numberContext);
return(
//
) } export default App;
<a name="nwRXT"></a>
# 5、useReducer
<a name="jEeKx"></a>
## 1、语法
const [state,dispatch] = useReducer(reducer,initArg,init) 第一个参数reducer是函数(state,action)=>newState 第二个参数initArg 初始状态值 第三个参数init 懒惰初始化函数
初始用法:
import { Button } from “antd”; import React, { useReducer } from “react”;
const index = () => { const reducer = (state, action) => { switch (action.type) { case “ADD”: return action.count + 1; case “PLUS”: return state - 1; default: return state; } }; const [count, dispatch] = useReducer(reducer, 0); return ( <> {count} </> ); };
export default index;
<a name="NWn2F"></a>
## 2、作用
**1、 useReducer 是 useState 的替代方案**,帮助我们集中式的处理复杂的state管理<br />例如 useState login版:
function LoginPage() { const [name, setName] = useState(‘’); // 用户名 const [pwd, setPwd] = useState(‘’); // 密码 const [isLoading, setIsLoading] = useState(false); // 是否展示loading,发送请求中 const [error, setError] = useState(‘’); // 错误信息 const [isLoggedIn, setIsLoggedIn] = useState(false); // 是否登录
const login = (event) => {
event.preventDefault();
setError('');
setIsLoading(true);
login({ name, pwd })
.then(() => {
setIsLoggedIn(true);
setIsLoading(false);
})
.catch((error) => {
// 登录失败: 显示错误信息、清空输入框用户名、密码、清除loading标识
setError(error.message);
setName('');
setPwd('');
setIsLoading(false);
});
}
return (
// 返回页面JSX Element
)
}
useReducer login版:
const initState = { name: ‘’, pwd: ‘’, isLoading: false, error: ‘’, isLoggedIn: false, } function loginReducer(state, action) { switch(action.type) { case ‘login’: return { …state, isLoading: true, error: ‘’, } case ‘success’: return { …state, isLoggedIn: true, isLoading: false, } case ‘error’: return { …state, error: action.payload.error, name: ‘’, pwd: ‘’, isLoading: false, } default: return state; } } function LoginPage() { const [state, dispatch] = useReducer(loginReducer, initState); const { name, pwd, isLoading, error, isLoggedIn } = state; const login = (event) => { event.preventDefault(); dispatch({ type: ‘login’ }); login({ name, pwd }) .then(() => { dispatch({ type: ‘success’ }); }) .catch((error) => { dispatch({ type: ‘error’ payload: { error: error.message } }); }); } return ( // 返回页面JSX Element ) } ``` 场景
- 如果你的state是一个数组或者对象
- 如果你的state变化很复杂,经常一个操作需要修改很多state
- 如果你希望构建自动化测试用例来保证程序的稳定性
- 如果你需要在深层子组件里面去修改一些状态
- 如果你用应用程序比较大,希望UI和业务能够分开维护
2、如果层级较深,useReducer可以搭载context使用,此时使用 dispatch 代替 callback 优势更明显。因为 dispatch 在 re-render 时不变,不会引起使用 context 的组件执行无意义的更新.
使用Context相比Callback的优势:
1、对比回调函数的自定义命名,Context的Api更加明确,我们可以更清晰的知道哪些组件使用了dispatch、应用中的数据流动和变化。这也是React一直以来单向数据流的优势。
2、更好的性能:如果使用回调函数作为参数传递的话,因为每次render函数都会变化,也会导致子组件rerender。当然我们可以使用useCallback解决这个问题,但相比useCallbackReact官方更推荐使用useReducer,因为React会保证dispatch始终是不变的,不会引起consumer组件的rerender。