Home > Enterprise >  React component not re-rendering when array prop changes
React component not re-rendering when array prop changes

Time:11-28

I want my App component to display keys every time keys changes. I'm doing this by passed keys as a prop of App:

import * as React from "react";
import { render } from "react-dom";
import { useState, useEffect } from "react"

let keys: string[] = [];

// This is what is supposed to happen in the real app
// document.addEventListener("keypress", (event) => {
  // keys.push(event.key)
// });

setTimeout(() => {
  keys.push('a');
}, 1000)

function App({ keys }: { keys: string[] }) {
  let [keysState, setKeysState] = useState(keys)

  useEffect(() => {
     setKeysState(keys)
  }, [keys])

  return (
    <div>
      {keysState.map((key: string) => (
        <li>{key}</li>
      ))}
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App keys={keys} />, rootElement);

However, App isn't re-rendering and displaying keys new value:

https://codesandbox.io/s/changing-props-on-react-root-component-forked-3mv0xf?file=/src/index.tsx

Why is this, and how to fix it?

Note: I tried: setKeysState([...keys, 'a']). That doesn't re-render App either.

Live code: https://codesandbox.io/s/changing-props-on-react-root-component-forked-3mv0xf?file=/src/index.tsx

CodePudding user response:

All data that is dynamic needs to be managed by React. Put your event inside the component and update local state.

function App({ initialKeys }: { initialKeys: string[] }) {
  const [keys, setKeys] = React.useState(initialKeys);
  console.log(keys);

  React.useEffect(() => {
    const append = (e) => {
      setKeys([...keys, e.key]);
    };
    document.addEventListener("keypress", append);
    return () => {
      document.removeEventListener("keypress", append);
    };
  }, [keys]);

  return (
    <div>
      {keys.map((key: string, idx) => (
        <li key={idx}>{key}</li>
      ))}
    </div>
  );
}

https://codesandbox.io/s/changing-props-on-react-root-component-forked-l869dd?file=/src/index.tsx

CodePudding user response:

if you use the below strategy it works as you want it to work. React cannot see state changes out of its built-in functions so it didn't track the change on your array which was out of its state scope

import * as React from "react";
import { render } from "react-dom";
import { useState, useEffect } from "react";

let keys: string[] = [];

function App(props: any) {
  const [keys, oldKeysState] = useState(props.keys);
  const [keysState, setKeysState] = useState(keys);

  useEffect(() => {
    setKeysState(keys);
  }, [keys]);

  // componentWillMount
  useEffect(() => {
    setTimeout(() => {
      oldKeysState([...keys, "a"]);
    }, 1000);
  }, []);

  return (
    <div>
      {keysState.map((key: string) => (
        <li>{key}</li>
      ))}
      <button onClick={() => setKeysState([...keysState, " "])}> </button>
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App keys={keys} />, rootElement);
  • Related