Why does react component do not re render after changing state

Viewed 64

index.js

const [todo, setToDo] = useState([]);
useEffect(async() => {
socket.on("receive_message", (message) => {
  let result = todos ? todos : [];
  if (message.sendBy === 'user') {
    let old_index;
    if (result.length > 0) {
      old_index = result.findIndex(item => item.id === message.senderId);
      if (0 >= result.length) {
        var k = 0 - result.length + 1;
        while (k--) {
          result.push(undefined);
        }
      }
      result.splice(0, 0, result.splice(old_index, 1)[0]);
      setTodos(result); // for testing
  }]
});  }, [todos]);

const render =
todos &&
todos.map((item) => {
  return (
    <li
      key={item.id}
      className={activeId.id === item.id ? "person active-user" : "person"}
      data-chat="person1"
      onClick={(e) => historyAccess(item)}
    >
      <div className="user">
        <img
          src={item.profile_pic ? item.profile_pic : "avatars/12.png"}
          alt="Profile Picture"
        />
      </div>
      <p className="name-time">
        <span className="name">{item.name ? item.name : ""}</span>
        {/* <span className="time">{}</span> */}
      </p>
    </li>
  );
});

useEffect is working fine and settodos is changing its value as array of objects but it does not re render. It is working absolutely fine and setTodos is changing it's value and working as expected then Why this is happening

1 Answers

Issue

Array.prototype.splice mutates the array in-place. You save a reference of your todos state to local variable result and then mutate it and save it back into state. The array reference never changes.

Also, useEffect hook callbacks can't be async.

Solution

  1. Shallow copy the array first, then mutate it. This is so React can reconcile that the state has actually updated.
  2. Since you don't await anything it's safe to remove the async declaration

Code:

const [todos, setToDo] = useState([]); // <-- todos is already initially defined

useEffect(() => {
  socket.on("receive_message", (message) => {
    const result = [...todos]; // <-- shallow copy todos

    if (message.sendBy === 'user') {
      ...
      result.splice(0, 0, result.splice(old_index, 1)[0]); // <-- then mutate
      setTodos(result);
    }
  });
}, [todos]);
Related