react hooks编程规范(内部使用)
创始人
2024-03-21 07:59:17
0

规范1、根据情况使用单个State变量还是多个

useState 的出现,让我们可以使用多个 state 变量来保存 state,比如:

写法一、

const [width, setWidth] = useState(100);
const [height, setHeight] = useState(100);
const [left, setLeft] = useState(0);
const [top, setTop] = useState(0);

写法二、

const [state, setState] = useState({width: 100,height: 100,left: 0,top: 0
});

如果使用单个 state 变量,每次更新 state 时需要合并之前的 state。因为 useState 返回的 setState 会替换原来的值。这一点和 Class 组件的 this.setState 不同。this.setState 会把更新的字段自动合并到 this.state 对象中。

const handleMouseMove = (e) => {setState((prevState) => ({...prevState,left: e.pageX,top: e.pageY,}))
};

使用多个 state 变量可以让 state 的粒度更细,更易于逻辑的拆分和组合。比如,我们可以将关联的逻辑提取到自定义 Hook 中:

function usePosition() {const [left, setLeft] = useState(0);const [top, setTop] = useState(0);useEffect(() => {// ...}, []);return [left, top, setLeft, setTop];
}

我们发现,每次更新 left top 也会随之更新。因此,把 top left拆分为两个 state 变量显得有点多余。
在使用 state 之前,我们需要考虑状态拆分的「粒度」问题。如果粒度过细,代码就会变得比较冗余。如果粒度过粗,代码的可复用性就会降低。那么,到底哪些 state 应该合并,哪些 state 应该拆分呢?我总结了下面两点:

  • 将完全不相关的 state 拆分为多组 state。比如size position
  • 如果某些 state 是相互关联的,或者需要一起发生改变,就可以把它们合并为一组 state。比如 lefttop

规范2、hooks依赖数组不得超过3个

在 React 中,除了 useEffect 外,接收依赖数组作为参数的 Hook 还有 useMemo、useCallback 和 useImperativeHandle。如果依赖数组依赖了过多东西,可能导致代码难以维护。见如下代码:

const refresh = useCallback(() => {// ...
}, [name, searchState, address, status, personA, personB, progress, page, size]);

不要说内部逻辑了,光是看到这一堆依赖就令人头大!如果项目中到处都是这样的代码,可想而知维护起来多么痛苦。如何才能避免写出这样的代码呢?

首先,你需要重新思考一下,这些 deps 是否真的都需要?看下面这个例子:

function Example({id}) {const requestParams = useRef({});requestParams.current = {page: 1, size: 20, id};const refresh = useCallback(() => {doRefresh(requestParams.current);}, []);useEffect(() => {id && refresh(); }, [id, refresh]); // 思考这里的 deps list 是否合理?
}

虽然 useEffect 的回调函数依赖了 id 和 refresh 方法,但是观察 refresh 方法可以发现,它在首次 render 被创建之后,永远不会发生改变了。因此,把它作为 useEffect 的 deps 是多余的。

其次,如果这些依赖真的都是需要的,那么这些逻辑是否应该放到同一个 hook 中?

function Example({id, name, address, status, personA, personB, progress}) {const [page, setPage] = useState();const [size, setSize] = useState();const doSearch = useCallback(() => {// ...}, []);const doRefresh = useCallback(() => {// ...}, []);useEffect(() => {id && doSearch({name, address, status, personA, personB, progress});page && doRefresh({name, page, size});}, [id, name, address, status, personA, personB, progress, page, size]);
}

可以看出,在 useEffect 中有两段逻辑,这两段逻辑是相互独立的,因此我们可以将这两段逻辑放到不同 useEffect 中:

useEffect(() => {id && doSearch({name, address, status, personA, personB, progress});
}, [id, name, address, status, personA, personB, progress]);useEffect(() => {page && doRefresh({name, page, size});
}, [name,  page, size]);

如果逻辑无法继续拆分,但是依赖数组还是依赖了过多东西,该怎么办呢?就比如我们上面的代码:

这段代码中的 useEffect 依赖了七个值,还是偏多了。仔细观察上面的代码,可以发现这些值都是「过滤条件」的一部分,通过这些条件可以过滤页面上的数据。因此,我们可以将它们看做一个整体,也就是我们前面讲过的合并 state:

const [filters, setFilters] = useState({name: "",address: "",status: "",personA: "",personB: "",progress: ""
});useEffect(() => {id && doSearch(filters);
}, [id, filters]);

如果 state 不能合并,在 callback 内部又使用了 setState 方法,那么可以考虑使用 setState callback 来减少一些依赖。比如:

const useValues = () => {const [values, setValues] = useState({data: {},count: 0});const [updateData] = useCallback((nextData) => {setValues({data: nextData,count: values.count + 1 // 因为 callback 内部依赖了外部的 values 变量,所以必须在依赖数组中指定它});},[values], );return [values, updateData];
};

上面的代码中,我们必须在 useCallback 的依赖数组中指定 values,否则我们无法在 callback 中获取到最新的 values 状态。但是,通过 setState 回调函数,我们不用再依赖外部的 values 变量,因此也无需在依赖数组中指定它。就像下面这样:

const useValues = () => {const [values, setValues] = useState({});const [updateData] = useCallback((nextData) => {setValues((prevValues) => ({data: nextData,count: prevValues.count + 1, // 通过 setState 回调函数获取最新的 values 状态,这时 callback 不再依赖于外部的 values 变量了,因此依赖数组中不需要指定任何值}));}, []); // 这个 callback 永远不会重新创建return [values, updateData];
};

总结如何减少依赖:

  • 去掉不必要的依赖。
  • 将 Hook 拆分为更小的单元,每个 Hook 依赖于各自的依赖数组。
  • 通过合并相关的 state,将多个依赖值聚合为一个。
  • 通过 setState 回调函数获取最新的 state,以减少外部依赖。

规范3、传递给子组件的保持不变的变量使用useCallback/useMemo

为了保持父组件传递给子组件的引用保持不变,从而避免子组件重新渲染,我们需要让传递给子组件不变的部分,使用useMemo或者useCallback;

const Example = () => {const onSubmit = useCallback(() => { // 考虑这里的 useCallback 是否必要?doSomething();}, []);return 
onSubmit}>
; };
const Example = (num:number) => {const totalNumber = useMemo(()=>5*8*9/2,[])return 
totalNumber }>
; };

规范4、使用生命周期函数hooks时,如果确定只调用一次,省略依赖数组

查看如下例子,如果按照eslint规范拓展 react-hooks/exhaustive-deps 需要添加handleGlobalClick 依赖。但是如果添加该依赖,会导致hasMission变化后,handleGlobalClick 引用发生变化,同时触发useEffect,重新绑定,解绑window全局点击事件,造成效率问题;

  //--点击全局关闭悬浮的任务预览(如果展开着)const [hasMission, setHasMission] = useState(false);const handleGlobalClick = useCallback(()=> {if(hasMission){alert('1');setHasMission(true)}}, [hasMission]);useEffect(()=>{window.addEventListener('click', handleGlobalClick, false);return ()=>{window.removeEventListener('click', handleGlobalClick, false);};//--按照eslint规范拓展 `react-hooks/exhaustive-deps` 需要添加handleGlobalClick 依赖}, [handleGlobalClick]);

正确做法,省略handleGlobalClick引用

useEffect(()=>{window.addEventListener('click', handleGlobalClick, false);return ()=>{window.removeEventListener('click', handleGlobalClick, false);};//--按照eslint规范拓展 `react-hooks/exhaustive-deps` 需要添加handleGlobalClick 依赖}, [dispatch, handleGlobalClick]);

相关内容

热门资讯

汽车油箱结构是什么(汽车油箱结... 本篇文章极速百科给大家谈谈汽车油箱结构是什么,以及汽车油箱结构原理图解对应的知识点,希望对各位有所帮...
美国2年期国债收益率上涨15个... 原标题:美国2年期国债收益率上涨15个基点 美国2年期国债收益率上涨15个基...
嵌入式 ADC使用手册完整版 ... 嵌入式 ADC使用手册完整版 (188977万字)💜&#...
重大消息战皇大厅开挂是真的吗... 您好:战皇大厅这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...
盘点十款牵手跑胡子为什么一直... 您好:牵手跑胡子这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游...
senator香烟多少一盒(s... 今天给各位分享senator香烟多少一盒的知识,其中也会对sevebstars香烟进行解释,如果能碰...
终于懂了新荣耀斗牛真的有挂吗... 您好:新荣耀斗牛这款游戏可以开挂,确实是有挂的,需要了解加客服微信8435338】很多玩家在这款游戏...
盘点十款明星麻将到底有没有挂... 您好:明星麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【5848499】很多玩家在这款游戏...
总结文章“新道游棋牌有透视挂吗... 您好:新道游棋牌这款游戏可以开挂,确实是有挂的,需要了解加客服微信【7682267】很多玩家在这款游...
终于懂了手机麻将到底有没有挂... 您好:手机麻将这款游戏可以开挂,确实是有挂的,需要了解加客服微信【8435338】很多玩家在这款游戏...