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

react hooks setState and class setState #7

Open
xiaohesong opened this issue Mar 22, 2019 · 3 comments
Open

react hooks setState and class setState #7

xiaohesong opened this issue Mar 22, 2019 · 3 comments

Comments

@xiaohesong
Copy link
Owner

xiaohesong commented Mar 22, 2019

前两天开代码评审,因为是一个小项目,在其中用到了hooks。有个同事看到了一个事件处理里有两个setState。就提出来了会有性能问题。当时我觉得是没有这个问题的,会有批处理,和class中的setState一样。
相关的解释可以看这两个链接:
Batching update in react-hooks #14259
State updates from useState hook not always collapsed? #14189

因为来自外部的更新,比如setTimeout不会进行批量处理,所以最好还是按照dan的建议使用reducer

@xiaohesong
Copy link
Owner Author

例子:
下面可以来证明他是有批处理的:

function Example() {
  const [count, setCounter] = useState(0);

  function increment() {
    setCounter((count) => count + 1); //  或者: setCounter(count + 1); 自己尝试着两者的区别
  }

  function handleClick() {
    increment();
    increment();
    increment();
  }

  return(<button onClick={handleClick}>{count}</button>)
}

@xiaohesong xiaohesong changed the title react hooks 的 setState 和 class中的setState react hooks setState and class setState Apr 16, 2020
@xiaohesong
Copy link
Owner Author

facebook/react#16295 (comment)
这个是针对useReducerdispatch,此行为会每次dispatchre-render

Correct me if I'm wrong, but this is my understanding of one of the things that this intentionally helps solve.

INCREMENT is pure and DECREMENT is impure. Or maybe I'm not understanding those terms correctly. Either way, DECREMENT is not desired.

const counterReducer = (state, action) => {
 switch (action.type) {
   case "INCREMENT":
      return { ...state, count: state.count + 1 };
   case "DECREMENT":
      state.count = state.count - 1;
     return { ...state };
    case "RESET":
      return { count: 0 };
    default:
      break;
  }
};

...

dispatch({type: 'INCREMENT'}) // state.count: 1
dispatch({type: 'INCREMENT'}) // state.count: 2
dispatch({type: 'INCREMENT'}) // state.count: 3
dispatch({type: 'INCREMENT'}) // state.count: 4
dispatch({type: 'INCREMENT'}) // state.count: 5

dispatch({type: 'DECREMENT'}) // state.count: 3
dispatch({type: 'DECREMENT'}) // state.count: 1
dispatch({type: 'DECREMENT'}) // state.count: -1 

Here's a sandbox example: https://codesandbox.io/s/determined-fermat-lvqbv?file=/src/App.js

@xiaohesong
Copy link
Owner Author

如果我说class setStatehooks setState有何区别?

  • class版本有回调函数,hooks版本没有。
state = {count: 0}
/** class */
setState({count: 1}, () => console.log(this.state.count))
/** hooks */
setState({count: 1})
  • class版本setState的数据会进行合并,hooks版本的是直接replace。
state = {count: 0, name: ''}
/** class */
setState({count: 1}, () => {
	console.log(this.state.count) // {count: 1, name: ''}
})
/** hooks */
setState({count: 1}) // {count: 1}
  • class版本的setState是直接改变 ,hooks是会有个比较,不相等的时候再去改变。
state = {user: {car: {items: 3}}}
/** class */
state.user.car.items = 5
setState(state, () => {
	console.log(this.state.user.car.items) // 5
})
/** hooks */
state.user.car.items = 6
setState(state) // items is 6
  • class版本的setState改变的是值的内容,hooks的是直接改变引用(重新返回了一个)
const [todos, setTodos] = useState(someTodosArray);

/** un-re-render */
const onClick = () => {
  todos[3].completed = true;
  setTodos(todos);
}

/** re-render */
const onClick = () => {
  const newTodos = todos.slice();
  newTodos[3].completed = true;
  setTodos(newTodos);
}

Blogged Answers: A (Mostly) Complete Guide to React Rendering Behavior

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

No branches or pull requests

1 participant