简体   繁体   中英

React DOM not updating on state change

I have a react component that will accept an arbitrary number of files, each with its own language. I want to be able to have a button that lets a user add as many files as they want, each with its own dropdown menu of languages. I've eliminated the file upload for now as I isolate the issue I'm having.

I've created quite a few similar components already without a problem, but for some reason I'm having major trouble with this one. When I change the language on the dropdown, I console log state and see that the language has updated in state, but it does not update on the DOM in my map method.

Here is my code

function App() {
  const [files, setFiles] = useState([]);

  const languages = ["English", "Spanish", "Arabic", "French"];

  const handleAddFile = e => {
    let newFiles = files;
    newFiles.push({
      content: null,
      preview: null,
      language: ""
    });
    setFiles(newFiles);
  };

  const changeLanguage = (event, index) => {
    let newFiles = files;
    newFiles[index].language = event.target.value;
    setFiles(newFiles);
  };

  console.log(files);

  return (
    <div>
      <p>Files</p>
      <p>Upload a file for as many languages as you have</p>
      {files.map((file, index) => (
        <div key={index}>
          <p>current language: {file.language}</p>
          <select
            value={file.language}
            onChange={event => changeLanguage(event, index)}
          >
            <option value="" disabled>
              Select a language
            </option>
            {languages.map((lang, i) => (
              <option value={lang} key={i}>
                {lang}
              </option>
            ))}
          </select>
        </div>
      ))}
      <button onClick={e => handleAddFile(e)}>+ Add File</button>
    </div>
  );
}

I've also made a codesandbox to reproduce the issue, but it actually further complicated things. When I run the code locally, I am able to add file instances just fine, but the language on the select element does not update on the DOM for any instance. In the codesandbox, nothing updates on the DOM unless I make an edit in the editor, then everything updates just as I'd expect, including the language. In all cases state is console logged exactly as I'd expect it to be. The component in the sandbox is identical to what I have locally, though locally it is embedded in another component.

https://codesandbox.io/s/ed4fx?file=/src/App.js:0-1248

The problem is in both methods handleAddFile and changeLanguage , you are directly accessing the state value files, so for updating you need to take the copy and do the changes and set the new values.

Updates you need to do in both methods

let newFiles = files;

To

let newFiles = [...files];

As @Brian Suggests, when you update deep nested values like language in the changeLanguage method. you can do something as shown below so it won't mutate the state.

const changeLanguage = (event, index) => {
  let newFiles = [...files];
  newFiles[index] = { ...newFiles[index], language: event.target.value };
  setFiles(newFiles);
};

Check this working codesandbox

Better explanation here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM