Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Day94:说一下React Hooks在平时开发中需要注意的问题和原因? #906

Open
Genzhen opened this issue Jul 9, 2020 · 6 comments
Labels
React teach_tag

Comments

@Genzhen
Copy link
Collaborator

Genzhen commented Jul 9, 2020

No description provided.

@Genzhen Genzhen added the React teach_tag label Jul 9, 2020
@Genzhen
Copy link
Collaborator Author

Genzhen commented Jul 9, 2020

每日一题会在下午四点在交流群集中讨论,五点 Github、交流群同步更新答案

1)不要在循环,条件或嵌套函数中调用Hook,必须始终在React函数的顶层使用Hook

这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。

2)使用useState时候,使用push,pop,splice等直接更改数组对象的坑

使用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封装的状态管理工具。

@hitao123
Copy link

hitao123 commented Jul 17, 2020

useContent 应该是 useContext

已修改哈,感谢提出错误

@GolderBrother
Copy link

GolderBrother commented Jul 30, 2020

// 简单实现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];
}

@yaohuangguan
Copy link

// 简单实现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不行吗

@mesterLi
Copy link

// 简单实现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不会覆盖上一次的值吗

@DaphnisLi
Copy link

DaphnisLi commented Feb 21, 2023

useState 不会自动合并状态,并且不推荐直接用state来改变下次状态
使用hooks时要在顶部函数使用,这是为了方式hooks作用于条件语句内,因为更新前后hooks要保证一致,这样做的目的是因为react要进行状态的可持久化,在构建fiber树的过程中,要将老树的hooks 链表转移到新树上,顺序不一致就会出错

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
React teach_tag
Projects
None yet
Development

No branches or pull requests

6 participants