avatarAvinash Ega

Summary

The article discusses using ES6 Map with React State hooks, emphasizing the need to create a new Map instance when updating state to ensure React correctly re-renders the component.

Abstract

The article introduces the concept of using ES6 Map in conjunction with React State hooks, highlighting the differences between state management in class components and functional components with hooks. It explains how the useState hook simplifies state management by allowing a more concise syntax compared to the traditional this.setState in class components. However, when using non-primitive data types like Map, developers must be cautious to not mutate the state directly, as React uses the Object.is algorithm for state comparison and may not re-render the component if the state reference remains unchanged. The article provides an example of how to properly update a Map in state by creating a new Map instance with the updated values, ensuring that React detects the change and triggers a re-render.

Opinions

  • The author suggests that using state hooks in React is more concise than writing class components.
  • The article conveys that developers should be mindful of React's state comparison mechanism when using non-primitive data types.
  • The author emphasizes the importance of cloning a Map rather than mutating it to avoid state update issues in React.
  • It is implied that understanding how React handles state updates is crucial for writing efficient and bug-free functional components with hooks.

Using ES6 Map with React State hooks

State hooks are a great addition to react that allows us to write stateful components concisely without the use of a class component. If you are familiar with React, you know that to use state, you’d have to write a class component —

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

The equivalent of the above using state hook would be as concise as -

function Example() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

The setCount method returned by the useState hook works similar to this.setState in the class component. They both pass the updated state to react and enqueue a re-render.

However, there is a difference — In case of setState, React will always re-render the component and it’s children(unless the shouldComponentUpdate lifecycle method returns false). With setCount, React will bail out on an update if the updated state passed is same as previous state. React uses Object.is algorithm to compare the state.

In this article, we will see how this impacts the usage of a ES6 Map(or any other non-primitive as a matter of fact) with the state hook. Consider a function component that uses a Map —

function MapComponent(){
  const [myMap, setMyMap] = useState(new Map()); //declaring and initializing state with an empty map
return(
  <ul>
    {[...myMap.keys()].map(k => (
      <li key={k}>myMap.get(k)</li>
    ))}
  </ul>
);
}

Note that, to loop over the map inside jsx, we can not use forEach since forEach returns an undefined value. So, we loop over the myMap.keys() which returns an array. Map preserves the order in which key-value pairs are inserted.

The set() method on the map adds or updates the entry and returns the map. So, let’s implement the updateMap method —

function MapComponent(){
  const [myMap, setMyMap] = useState(new Map());
  const updateMap = (k,v) => {
    setMyMap(myMap.set(k,v));
  }
  return(
    <ul>
      {[...myMap.keys()].map(k => (
        <li key={k}>myMap.get(k)</li>
      ))}
    </ul>
  );
}

myMap.set() updates the map and setMyMap sets the state and tells React to re-render the component. Except that in this case, React doesn’t trigger a render. Any guesses ?

You’re right. React does a comparison of new state and old state and in this case, it compares the references of new Map to old Map which share the same value. So, React bails out without triggering an update.

What we need to pass to setMyMap is a clone of the map instead of a copy of the reference. A simple change to the setMyMap will do the trick —

function MapComponent(){
  const [myMap, setMyMap] = useState(new Map());
  const updateMap = (k,v) => {
    setMyMap(new Map(myMap.set(k,v)));
  }
  return(
    <ul>
      {[...myMap.keys()].map(k => (
        <li key={k}>myMap.get(k)</li>
      ))}
    </ul>
  );
}
JavaScript
React
React Hook
ES6
Reactjs
Recommended from ReadMedium