We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
每日一题会在下午四点在交流群集中讨论,五点 Github、交流群同步更新答案
1)不要在循环,条件或嵌套函数中调用Hook,必须始终在React函数的顶层使用Hook
这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。
2)使用useState时候,使用push,pop,splice等直接更改数组对象的坑
useState
使用push直接更改数组无法获取到新值,应该采用析构方式,但是在class里面不会有这个问题
代码示例
function Indicatorfilter() { let [num,setNums] = useState([0,1,2,3]) const test = () => { // 这里坑是直接采用push去更新num // setNums(num)是无法更新num的 // 必须使用num = [...num ,1] num.push(1) // num = [...num ,1] setNums(num) } return ( <div className='filter'> <div onClick={test}>测试</div> <div> {num.map((item,index) => ( <div key={index}>{item}</div> ))} </div> </div> ) } class Indicatorfilter extends React.Component<any,any>{ constructor(props:any){ super(props) this.state = { nums:[1,2,3] } this.test = this.test.bind(this) } test(){ // class采用同样的方式是没有问题的 this.state.nums.push(1) this.setState({ nums: this.state.nums }) } render(){ let {nums} = this.state return( <div> <div onClick={this.test}>测试</div> <div> {nums.map((item:any,index:number) => ( <div key={index}>{item}</div> ))} </div> </div> ) } }
3)useState设置状态的时候,只有第一次生效,后期需要更新状态,必须通过useEffect
看下面的例子
TableDeail是一个公共组件,在调用它的父组件里面,我们通过set改变columns的值,以为传递给TableDeail的columns是最新的值,所以tabColumn每次也是最新的值,但是实际tabColumn是最开始的值,不会随着columns的更新而更新
const TableDeail = ({ columns, }:TableData) => { const [tabColumn, setTabColumn] = useState(columns) } // 正确的做法是通过useEffect改变这个值 const TableDeail = ({ columns, }:TableData) => { const [tabColumn, setTabColumn] = useState(columns) useEffect(() =>{setTabColumn(columns)},[columns]) }
4)善用useCallback
父组件传递给子组件事件句柄时,如果我们没有任何参数变动可能会选用useMemo。但是每一次父组件渲染子组件即使没变化也会跟着渲染一次。
5)不要滥用useContext
可以使用基于useContext封装的状态管理工具。
useContent 应该是 useContext 已修改哈,感谢提出错误
useContent 应该是 useContext
已修改哈,感谢提出错误
// 简单实现hooks // 一、实现useState const { render } = require("react-dom"); let memoriedStates = []; let lastIndex = 0; function useState(initialState) { memoriedStates[lastIndex] = memoriedStates[lastIndex] || initialState; function setState(newState) { memoriedStates[lastIndex] = newState; // 状态更新完毕,调用render函数。重新更新视图 render(); } // 返回最新状态和更新函数,注意index要前进 return [memoriedStates[lastIndex++], setState]; } // 二、实现useEffect let lastDendencies; // 存放依赖项的数组 function useEffect(callback, dependencies) { if (lastDendencies) { // 判断传入的依赖项是不是都没有变化,只要有以一项改变,就需要执行callback const isChange = dependencies && dependencies.some((dep, index) => dep !== lastDendencies[index]); if (isChange) { // 一开始没有值,需要更新一次(相当于componentDidMount) typeof callback === 'function' && callback(); // 更新依赖项 lastDendencies = dependencies; } } else { // 一开始没有值,需要更新一次(相当于componentDidMount) typeof callback === 'function' && callback(); // 更新依赖项 lastDendencies = dependencies; } } // 三、实现useCallback let lastCallback; // 最新的回调函数 let lastCallbackDependencies = []; // 回调函数的依赖项 function useCallback(callback, dependencies = []) { if (lastCallback) { const isChange = dependencies && dependencies.some((dep, index) = dep !== lastCallbackDependencies[index]); if (isChange) { // 只要有一个依赖项改变了,就更新回调(重新创建) lastCallback = callback; lastCallbackDependencies = dependencies; } } else { lastCallback = callback; lastCallbackDependencies = dependencies; } // 最后需要返回最新的函数 return lastCallback; } // 四、实现useRef let lastRef; function useRef(initialValue = null){ lastRef = lastRef != undefined ? lastRef : initialValue; // 本质上就是返回一个对象,对象种有一个current属性,值为初始化传入的值,如果没有传入初始值,则默认为null return { current: lastRef } } // 五、实现useContext function useContext(context){ // 很简单,就是返回context的_currentValue值 return context._currentValue; } // 六、实现useReducer let lastState; function useReducer(reducer, initialState){ lastState = lastState !== undefined ? lastState : initialState; // dispatch一个action,内部就是自动调用reducer来计算新的值返回 function dispatch(action){ lastState = reducer(lastState, action); // 更新完毕后,需要重新渲染视图 render(); } // 最后返回一个的状态值和派发action的方法 return [lastState, dispatch]; }
1.useState, 为啥index要前进?0不行吗
// 简单实现hooks // 一、实现useState const { render } = require("react-dom"); let memoriedStates = []; let lastIndex = 0; function useState(initialState) { memoriedStates[lastIndex] = memoriedStates[lastIndex] || initialState; function setState(newState) { memoriedStates[lastIndex] = newState; // 状态更新完毕,调用render函数。重新更新视图 render(); } // 返回最新状态和更新函数,注意index要前进 return [memoriedStates[lastIndex++], setState]; } // 二、实现useEffect let lastDendencies; // 存放依赖项的数组 function useEffect(callback, dependencies) { if (lastDendencies) { // 判断传入的依赖项是不是都没有变化,只要有以一项改变,就需要执行callback const isChange = dependencies && dependencies.some((dep, index) => dep !== lastDendencies[index]); if (isChange) { // 一开始没有值,需要更新一次(相当于componentDidMount) typeof callback === 'function' && callback(); // 更新依赖项 lastDendencies = dependencies; } } else { // 一开始没有值,需要更新一次(相当于componentDidMount) typeof callback === 'function' && callback(); // 更新依赖项 lastDendencies = dependencies; } } // 三、实现useCallback let lastCallback; // 最新的回调函数 let lastCallbackDependencies = []; // 回调函数的依赖项 function useCallback(callback, dependencies = []) { if (lastCallback) { const isChange = dependencies && dependencies.some((dep, index) = dep !== lastCallbackDependencies[index]); if (isChange) { // 只要有一个依赖项改变了,就更新回调(重新创建) lastCallback = callback; lastCallbackDependencies = dependencies; } } else { lastCallback = callback; lastCallbackDependencies = dependencies; } // 最后需要返回最新的函数 return lastCallback; } // 四、实现useRef let lastRef; function useRef(initialValue = null){ lastRef = lastRef != undefined ? lastRef : initialValue; // 本质上就是返回一个对象,对象种有一个current属性,值为初始化传入的值,如果没有传入初始值,则默认为null return { current: lastRef } } // 五、实现useContext function useContext(context){ // 很简单,就是返回context的_currentValue值 return context._currentValue; } // 六、实现useReducer let lastState; function useReducer(reducer, initialState){ lastState = lastState !== undefined ? lastState : initialState; // dispatch一个action,内部就是自动调用reducer来计算新的值返回 function dispatch(action){ lastState = reducer(lastState, action); // 更新完毕后,需要重新渲染视图 render(); } // 最后返回一个的状态值和派发action的方法 return [lastState, dispatch]; } 1.useState, 为啥index要前进?0不行吗 是为了下一个useState不会覆盖上一次的值吗
1.useState, 为啥index要前进?0不行吗 是为了下一个useState不会覆盖上一次的值吗
useState 不会自动合并状态,并且不推荐直接用state来改变下次状态 使用hooks时要在顶部函数使用,这是为了方式hooks作用于条件语句内,因为更新前后hooks要保证一致,这样做的目的是因为react要进行状态的可持久化,在构建fiber树的过程中,要将老树的hooks 链表转移到新树上,顺序不一致就会出错
Activity
Genzhen commentedon Jul 9, 2020
1)不要在循环,条件或嵌套函数中调用Hook,必须始终在React函数的顶层使用Hook
这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。
2)使用
useState
时候,使用push,pop,splice等直接更改数组对象的坑使用push直接更改数组无法获取到新值,应该采用析构方式,但是在class里面不会有这个问题
代码示例
3)useState设置状态的时候,只有第一次生效,后期需要更新状态,必须通过useEffect
看下面的例子
TableDeail是一个公共组件,在调用它的父组件里面,我们通过set改变columns的值,以为传递给TableDeail的columns是最新的值,所以tabColumn每次也是最新的值,但是实际tabColumn是最开始的值,不会随着columns的更新而更新
4)善用useCallback
父组件传递给子组件事件句柄时,如果我们没有任何参数变动可能会选用useMemo。但是每一次父组件渲染子组件即使没变化也会跟着渲染一次。
5)不要滥用useContext
可以使用基于useContext封装的状态管理工具。
hitao123 commentedon Jul 17, 2020
GolderBrother commentedon Jul 30, 2020
yaohuangguan commentedon May 11, 2021
1.useState, 为啥index要前进?0不行吗
mesterLi commentedon May 13, 2021
DaphnisLi commentedon Feb 21, 2023
useState 不会自动合并状态,并且不推荐直接用state来改变下次状态
使用hooks时要在顶部函数使用,这是为了方式hooks作用于条件语句内,因为更新前后hooks要保证一致,这样做的目的是因为react要进行状态的可持久化,在构建fiber树的过程中,要将老树的hooks 链表转移到新树上,顺序不一致就会出错