React DOM 不更新状态变化
React DOM not updating on state change
我有一个 React 组件,可以接受任意数量的文件,每个文件都有自己的语言。我希望能够有一个按钮,让用户可以添加任意数量的文件,每个文件都有自己的语言下拉菜单。我暂时取消了文件上传,因为我隔离了我遇到的问题。
我已经毫无问题地创建了很多类似的组件,但出于某种原因,我在这个组件上遇到了很大的麻烦。当我更改下拉菜单中的语言时,我控制日志状态并看到该语言的状态已更新,但它不会在我的地图方法中的 DOM 上更新。
这是我的代码
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>
);
}
我也做了一个codesandbox来重现这个问题,但它实际上使事情变得更加复杂。当我在本地 运行 代码时,我可以很好地添加文件实例,但是 select 元素上的语言不会在任何实例的 DOM 上更新。在 codesandbox 中,DOM 上没有任何更新,除非我在编辑器中进行编辑,然后所有内容都会按照我的预期进行更新,包括语言。在所有情况下,状态都是控制台记录的,完全符合我的预期。沙箱中的组件与我本地的相同,尽管在本地它嵌入了另一个组件。
问题出在两种方法 handleAddFile
和 changeLanguage
中,您正在直接访问状态值文件,因此要进行更新,您需要获取副本并进行更改并设置新值.
两种方法都需要更新
let newFiles = files;
到
let newFiles = [...files];
正如@Brian 建议的那样,当您在 changeLanguage
方法中更新像语言这样的深层嵌套值时。您可以执行如下所示的操作,这样它就不会改变状态。
const changeLanguage = (event, index) => {
let newFiles = [...files];
newFiles[index] = { ...newFiles[index], language: event.target.value };
setFiles(newFiles);
};
检查这个工作codesandbox
更好的解释here
我有一个 React 组件,可以接受任意数量的文件,每个文件都有自己的语言。我希望能够有一个按钮,让用户可以添加任意数量的文件,每个文件都有自己的语言下拉菜单。我暂时取消了文件上传,因为我隔离了我遇到的问题。
我已经毫无问题地创建了很多类似的组件,但出于某种原因,我在这个组件上遇到了很大的麻烦。当我更改下拉菜单中的语言时,我控制日志状态并看到该语言的状态已更新,但它不会在我的地图方法中的 DOM 上更新。
这是我的代码
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>
);
}
我也做了一个codesandbox来重现这个问题,但它实际上使事情变得更加复杂。当我在本地 运行 代码时,我可以很好地添加文件实例,但是 select 元素上的语言不会在任何实例的 DOM 上更新。在 codesandbox 中,DOM 上没有任何更新,除非我在编辑器中进行编辑,然后所有内容都会按照我的预期进行更新,包括语言。在所有情况下,状态都是控制台记录的,完全符合我的预期。沙箱中的组件与我本地的相同,尽管在本地它嵌入了另一个组件。
问题出在两种方法 handleAddFile
和 changeLanguage
中,您正在直接访问状态值文件,因此要进行更新,您需要获取副本并进行更改并设置新值.
两种方法都需要更新
let newFiles = files;
到
let newFiles = [...files];
正如@Brian 建议的那样,当您在 changeLanguage
方法中更新像语言这样的深层嵌套值时。您可以执行如下所示的操作,这样它就不会改变状态。
const changeLanguage = (event, index) => {
let newFiles = [...files];
newFiles[index] = { ...newFiles[index], language: event.target.value };
setFiles(newFiles);
};
检查这个工作codesandbox
更好的解释here