美文网首页
useState: async/copy then setSta

useState: async/copy then setSta

作者: Time_Notes | 来源:发表于2023-09-15 01:41 被阅读0次

React hooks are used by functional components to provide the same functionality as class components.

Since the functional component does not store or handle the state, it is also referred to as a stateless component. React does, however, provide a hook called useState() that enables function components to keep track of their state.


5.1 state update is not sync#

The set function only updates the state variable for the next render. If you read the state variable after calling the set function, you will still get the old value that was on the screen before your call.

This is easy to understand, we’ve already seen how setState() schedules re-render in next tick, it is not sync and the updated value can only be got from next render since the state update is done in useState() not in setState().


Key Concept is that setState is an async call.

https://stackoverflow.com/questions/55373878/what-are-the-differences-when-re-rendering-after-state-was-set-with-hooks-compar

Is it correct that this.setState in class components always cause a re-render, even when the new state value is identical to the previous?

If you set a valid value apart from returning null within setState, a re-render will always be triggered by react in a class component unless your component is a PureComponent or you implement shouldComponentUpdate

Is it correct that in function components with hooks, setState from useState only causes a re-render if the state value is different from the previous value?

For a functional component using useState hook, the setter if called with the same state will not trigger a re-render. However for an occasional case if the setter is called immediately it does result in two renders instead of one

Is setting state with this.setState inside the render method of a class component, the same as setting state inside the function body of a function component with hooks?

Techincally yes, setting a state directly in render method will cause the function to trigger re-render in case of class component causing an infinite loop which is the case for functional components provided the state values are different. Regardless of that, it will still cause an issue because any other state update will be reverted back due to functional component calling state update directly

In a class component, if we set state in the render method an infinite loop will occur. This is because the class component does not care that the new state is the same as the previous state. It just keeps re-rendering on every this.setState.

Yes, hence its recommended not to call setState directly in render

In a function component with hooks however, setting state inside the function body (which runs at re-render similarly to the render method in class components) would not be an issue, because the function component just bails out of re-renders when it sees that the state is identical to the previous state.

Not 100% true, since you can trigger state update using previous value such that the previous and current value are not same. For example

setCount(count=>count +1);

In such a case, you component will still fall in an infinite loop


5.2 setState() with same value might still trigger re-render.#

If the new value you provide is identical to the current state, as determined by an Object.is comparison, React will skip re-rendering the component and its children. This is an optimization. Although in some cases React may still need to call your component before skipping the children, it shouldn’t affect your code.


5.3 React batches state updates#

React batches state updates. It updates the screen after all the event handlers have run and have called their set functions. This prevents multiple re-renders during a single event. In the rare case that you need to force React to update the screen earlier, for example to access the DOM, you can use flushSync.

As illustrated in the previous slides, updates are stashed before they are actually processed, then they will be processed together.


useState如何和Class写法相对应:

类组件有一个大的state对象,函数this.setState一次改变整个state对象。

class MyCard extends React.Component{

    constructor(props){

        super(props);

        this.state={name:'Niko'};

        this.handleNameChange=this.handleNameChange.bind(this);

    }

    handleNameChange(e){

        this.setState({name:e.target.name})

    }

    render(){

        return(

            <div>

                <input type="text" 

                    value={this.state.name}

                    onChange={this.handleNameChange}

                />

            </div>

        )

    }

}

使用hook:

函数组件没有状态,但useState允许我们在需要时添加很小的状态块。

function MyCard(){

    const [name,setName] = userState('Niko')

    return(

        <div>

            <input type="text" 

                value={name}

                onChange={(e)=>setName(e.target.value)}

            />        

        </div>

    )

}


The correct way to update the state object in React when using useState

The suggested approach for updating the state object in React when using useState is to copy the state object and then update the copy.

This usually involves using the spread operator (...).

Keeping this in mind, here's the updated code:

import { useState } from "react"; 

export default function App() { 

  const [greeting, setGreeting] = useState({ greet: "Hello, World" }); 

  console.log(greeting, setGreeting); 

  function updateGreeting() { 

    const newGreeting = {...greeting}; //make a copy, do not directly use state variable

This is because of how React optimize its virtual DOM. Keeping state immutable makes it possible to compare a previous version of virtual DOM with the updated version more efficiently and cost effectively.

    newGreeting.greet = "Hello, World-Wide Web"; 

    setGreeting(newGreeting); 

  } 

  return ( 

    <div> 

      <h1>{greeting.greet}</h1> 

      <button onClick={updateGreeting}>Update greeting</button> 

    </div> 

  ); 

To reiterate, the proper way of working with state when it's saved as an object is to:

1.Copy the old state object using the spread (...) operator and save it into a new variable and 

2.Pass the new variable to the state-updating function 

Incorrect ways of trying to update the state object

To prove that a copy of the old state object is needed to update state, let’s explore what happens when you try to update the old state object directly:

  function updateGreeting() { 

    greeting = {greet: "Hello, World-Wide Web}; 

    setGreeting(greeting); 

  } 

The above code does not work because it has a TypeError hiding inside of it.

Specifically, the TypeError is: "Assignment to constant variable".

In other words, you cannot reassign a variable declared using const, such as in the case of the useState hook's array destructuring:

const [greeting, setGreeting] = useState({ greet: "Hello, World" }); 

Another approach you might attempt to use to work around the suggested way of updating state when working with a state object might be the following:  

function updateGreeting() { 

    greeting.greet = "Hello, World-Wide Web; 

    setGreeting(greeting); 

  }

The above code is problematic because it doesn't throw any errors; however, it also doesn't update the heading, so it is not working correctly. This means that, regardless of how many times you click the "Update greeting" button, it will still be "Hello, World".

Updating the state object using arrow functions

Now, let’s use a more complex object to update state.

The state object now has two properties: greet and location.

The intention of this update is to demonstrate what to do when only a specific property of the state object is changing, while keeping the remaining properties unchanged:

import { useState } from "react"; 

export default function App() { 

  const [greeting, setGreeting] = useState( 

    { 

        greet: "Hello", 

        place: "World" 

    } 

  ); 

  console.log(greeting, setGreeting); 

  function updateGreeting() { 

    setGreeting(prevState => { 

        return {...prevState, place: "World-Wide Web"} 

    }); 

  } 

  return ( 

    <div> 

      <h1>{greeting.greet}, {greeting.place}</h1> 

      <button onClick={updateGreeting}>Update greeting</button> 

    </div> 

  ); 

The reason this works is because it uses the previous state, which is named prevState, and this is the previous value of the greeting variable. In other words, it makes a copy of the prevState object, and updates only the place property on the copied object. It then returns a brand-new object: 

return {...prevState, place: "World-Wide Web"} 

Everything is wrapped in curly braces so that this new object is built correctly, and it is returned from the call to setGreeting.


Updating state based on the previous state

Suppose theageis42. This handler callssetAge(age + 1)three times:

function handleClick(){

setAge(age+1);// setAge(42 + 1)

setAge(age+1);// setAge(42 + 1)

setAge(age+1);// setAge(42 + 1)

}

However, after one click,agewill only be43rather than45! This is because calling thesetfunctiondoes not updatetheagestate variable in the already running code. So eachsetAge(age + 1)call becomessetAge(43).

To solve this problem, you may pass an updater function to setAge instead of the next state:

function handleClick(){

setAge(a=>a + 1);// setAge(42 => 43)

setAge(a=>a + 1);// setAge(43 => 44)

setAge(a=>a + 1);// setAge(44 => 45)

}

Here,a => a + 1is your updater function. It takes thepending stateand calculates thenext statefrom it.

React puts your updater functions in aqueue.Then, during the next render, it will call them in the same order:

1.a => a + 1 will receive 42 as the pending state and return 43 as the next state.

2.a => a + 1 will receive 43 as the pending state and return 44 as the next state.

3.a => a + 1 will receive 44 as the pending state and return 45 as the next state.

There are no other queued updates, so React will store45as the current state in the end.

By convention, it’s common to name the pending state argument for the first letter of the state variable name, likeaforage. However, you may also call it likeprevAgeor something else that you find clearer.

React maycall your updaters twicein development to verify that they arepure.

Is using an updater always preferred? 

You might hear a recommendation to always write code like setAge(a => a + 1)if the state you’re setting is calculated from the previous state. There is no harm in it, but it is also not always necessary.

In most cases, there is no difference between these two approaches. React always makes sure that for intentional user actions, like clicks, the age state variable would be updated before the next click. This means there is no risk of a click handler seeing a “stale” age at the beginning of the event handler.

However, if you do multiple updates within the same event, updaters can be helpful. They’re also helpful if accessing the state variable itself is inconvenient (you might run into this when optimizing re-renders).

If you prefer consistency over slightly more verbose syntax, it’s reasonable to always write an updater if the state you’re setting is calculated from the previous state. If it’s calculated from the previous state of someotherstate variable, you might want to combine them into one object anduse a reducer.


useRef vs. useState

Both useRef and useState persist a value across re-renders of a component. This means the value doesn’t get reset when the component re-renders, whereas all local variables go into a time loop.

The value tracked by useState is updated via calling the setter function, which triggers a re-render of the component. In comparison, the value tracked by useRef is updated via direct mutation, which does not trigger a re-render.


当组件重新渲染时,每次都不会重新创建新的状态吗?

React如何知道旧状态是什么?

useState can be obj

相关文章

网友评论

      本文标题:useState: async/copy then setSta

      本文链接:https://www.haomeiwen.com/subject/pusqwctx.html