1. Setting the state using the previous state is unpredictable

State management is the foundation of React, and while useState is probably the most common hook, you may not know what it actually does. Let’s look at the following components:

import React, { useState } from "react";
import "./styles.css";

export default function App() {
  const [counter, setCounter] = useState(0);
  return (
    <div className="App">
      <h1>Counter: {counter}</h1>
      <button
        onClick={()= >{ setCounter(counter + 1); setCounter(counter + 1); > +}}</button>
    </div>
  );
}
Copy the code

What do you want the value of the counter state to be after the user clicks the button? A. 2 B. 1 ✔️

Click on the demo

The reason is that during our status update, we used the previous status value: setCounter(count + 1). Essentially, the setState function is wrapped in a functional component closure, so it provides the values captured in that closure. This means that when it is finally executed (the setState function is asynchronous), it may hold a state value that is no longer relevant. On top of that, the sequential execution of setState could cause the React scheduling algorithm to handle multiple very fast state updates using the same event handler. The same problem can occur when setting state in asynchronous functions:

onClick={() = > { 
   setTimout(() = > { setCounter(counter + 1); .1000); 
}};
Copy the code

But don’t worry, React actually provides a simple solution to this problem — “Functional Updates.” setCounter((prevCounter) => prevCounter + 1);

Note: Be sure to use functional updates whenever your status update depends on the previous status!

// incorrect
const update = useCallback(() = > {
   setCounter(counter + 1);
}, [counter]);

/ / correct ✔ ️
const update = useCallback(() = > {
   setCounter(prevCounter= > prevCounter + 1); } []);Copy the code

2. Use useRef to store static variables

We’re used to using the REF mechanism in React as a means of accessing an element’s DOM nodes, whether because we need it to calculate its size, set focus state, or basically do anything React naturally can’t do. But refs can also be used for a different purpose — we can do this very easily with class components, but we can’t use functional components — keeping a static variable that isn’t recreated every time we render.

Click on the demo

We can use refs to store static variables in functional components

3. React Forces a component to be remounted

DOM writing is very expensive. This is why we usually don’t want to remount components unless absolutely necessary. But sometimes we have to, for various reasons. So in that case, how do we tell React to uninstall and immediately remount the component? Use a simple trick — give our component a key and change its value.

Key Prop is a special React property

The famous React warning

Key is what helps React keep track of elements, even if we’ve changed its position in the component structure or re-rendered the parent (otherwise each rendering would result in the entire component array being reinstalled, which is bad performance).

Using this mechanism, we can trick React into thinking that a component is different from its previous self and cause it to remount.

Click on the demo

import React, { useEffect, useState, useCallback } from "react";
import "./styles.css";

export default function App() {
  const [key, setKey] = useState(1);
  const [console, setConsole] = useState([]);

  const onLifecycleChange = useCallback((message) = > {
    setConsole((prevConsole) = >[message, ...prevConsole]); } []);return (
    <div className="App">
      <button
        onClick={()= > {
          setKey((oldKey) => oldKey + 1);
        }}
      >
        Remount
      </button>
      <ChildComp key={key} onLifecycleChange={onLifecycleChange} />

      <div className="console">
        {console.map((text, i) => (
          <div key={i}>{text}</div>
        ))}
      </div>
    </div>
  );
}

const ChildComp = React.memo(({ onLifecycleChange }) = > {
  useEffect(() = > {
    onLifecycleChange("mounting ChildComp");
    return () = > {
      onLifecycleChange("ummounting ChildComp");
    };
  }, [onLifecycleChange]);

  return <div style={{ marginTop: 10}} >Child Comp</div>;
});
Copy the code

4.Context doesn’t work like you expect it to

Context is used to solve the “prop Drilling” problem, but it can cause performance problems. A change in the state of one of the properties causes all other components subscribed to the Context to be updated. So context is usually used for scenarios that don’t update very often like (locale and theme)

  • Use-context-selector can solve performance problems caused by context

  • You are advised to use a status management tool such as Redux if you frequently update status (status sharing)

import React, { useState, useContext } from "react";
import "./styles.css";

const SomeContext = React.createContext({});

export default function App() {
  const [contextValue, setContextValue] = useState({ name: "John".age: 55 });

  const onChangeAge = (e) = > {
    const age = e.target.value;
    setContextValue((prevContextValue) = > ({ ...prevContextValue, age }));
  };

  const onChangeName = (e) = > {
    const name = e.target.value;
    setContextValue((prevContextValue) = > ({ ...prevContextValue, name }));
  };

  return (
    <div className="App">
      <SomeContext.Provider value={contextValue}>
        <Wrapper />
        <input value={contextValue.age} onChange={onChangeAge} />
        <input value={contextValue.name} onChange={onChangeName} />
      </SomeContext.Provider>
    </div>
  );
}

const Wrapper = () = > {
  return (
    <div>
      <Name />
      <Age />
    </div>
  );
};

const Name = () = > {
  const { name } = useContext(SomeContext);
  console.log("name rendered");
  return <h1>Name: {name}</h1>;
};

const Age = () = > {
  const { age } = useContext(SomeContext);
  console.log("age rendered");
  return <h1>Age: {age}</h1>;
};

Copy the code

Click on the demo

5. React has a full API to handle the children property

React provides a series of apis for the Children property

React.Children.toArray(children)
// If you want to use map/forEach:
React.Children.map(children, fn)
React.Children.forEach(children, fn)
Copy the code
React.Children.count(children)
Copy the code

If you need to enforce a single subitem in your component (I recently noticed Formik doing this), you can simply include the following line in your component and React will run checks and error handling for you:

React.Children.only(children)
Copy the code
import React from "react";
import "./styles.css";

export default function App() {
  return (
    <div className="App">
      <Wrapper>
        <h2 style={{ color: "red", margin: 0}} >Red</h2>
        <h2 style={{ color: "blue}} ">Blue</h2>
        <h2 style={{ color: "green}} ">Green</h2>
      </Wrapper>
      <Wrapper>hello</Wrapper>
    </div>
  );
}

const Wrapper = ({ children }) = > {
  const childrenArray = React.Children.toArray(children);
  console.log(childrenArray);
  return (
    <div style={{ border: "1px solid", padding: 20.margin: 5}} >
      <div>{children}</div>
      <div>Number of children: {React.Children.count(children)}</div>
      <div>
        children type: <strong>{typeof children}</strong>
      </div>
    </div>
  );
};
Copy the code

Click on the demo

reference

  • Medium.com/geekculture…