我目前正在使用 React (TSX),并遇到了有关状态的问题。我对这个话题相当陌生,所以如果这是明显需要解决的问题,我深表歉意..
考虑一个回调函数 testComponent,当按下按钮时,每次都会渲染一个新组件。
在此组件内,将有一个选择标签和一个输入标签,并且根据选择的内容,输入标签也必须更新。
例如,选择标签将显示对象的名称,输入标签将显示其 ID。
并且,所有 selectedOptions 都将以 options[0] 的默认值开始,这发生在 useEffect 中。
这是我的问题的重现,只是使用“测试”一词来代替,以便更容易理解(需要更少的上下文)。这个想法是,每次选择元素发生变化时,它也应该更新输入标签。
export default function CreateTestComponent() {
const options: any[] = [
{
id: 1,
name: "Test 1"
},
{
id: 2,
name: "Test 2",
},
{
id: 3,
name: "Test 3"
}
]
const [selectedOptions, setSelectedOptions] = useState<any[]>([options[0]]);
const [testComponentIndex, setTestComponentIndex] = useState<number>(0);
const [components, setComponents] = useState<any[]>([]);
useEffect(() => {
setComponents([testComponent(0)]);
setTestComponentIndex((old) => old + 1);
for (let i = 0; i < options.length; i++) {
setSelectedOptions((old) => {
const temp = [...old];
temp[i] = options[0];
return temp
})
}
}, [])
const testComponent = (index: number) => {
return (
<div className="flex flex-row gap-5" id={`${index}`}>
<select
onChange={((e) => {
const id = e.target.value;
setSelectedOptions((old) => {
const temp = [...old]
temp[index] = options.filter((option) => option.id == id)[0];
return temp;
})
})}>
{options.map((option, index: number) => {
return (
<option key={index} value={option.id}>
{option.name}
</option>
);
})}
</select>
<input readOnly value={selectedOptions[index].id} />
</div>
)
}
return (
<>
<button type="button" onClick={() => {
setTestComponentIndex((old) => old + 1)
setComponents([...components, testComponent(testComponentIndex)]);
}} className="bg-black text-white rounded px-3 py-1">
Add a Component
</button>
<div>
<h1>Component testing!</h1>
<div>
<ul className="list-none">
{components.map((component: any, index: number) => {
return (
<li key={index}>
<div className="flex flex-row gap-5">
{component}
</div>
</li>
)
})}
</ul>
</div>
</div>
</>
)
}
此代码将在 .tsx 文件中运行。
如您所见,状态正在更新,但输入标签没有更新。我做了大量令人烦恼的研究,试图弄清楚发生了什么,而且我非常确定,因为它位于回调函数内,其中状态不会持续更新(这反过来会触发重新渲染)。
我尝试做很多事情来绕过这个障碍。也就是说,我尝试了 useRef(),但它不具有重新渲染的功能,而只有 useState 似乎可以做到这一点。
我经历了很多其他事情,但没有一个解决了这个问题,因为他们都没有解决这个问题,即回调函数外部和内部的状态不同。
如果无法在回调函数中保持最新状态,我可以尝试哪些其他替代方法,以便我仍然能够按下按钮并每次生成组件的新实例?
谢谢
我做了一些更改,应该可以正常工作。变化是:-
将 TestComponent 分离为子组件,其中包含索引、selectedOption 和 handleSelectChange 函数的属性。
handleSelectChange 通过映射当前选项来更新状态中的正确选项。
AddComponent 将 TestComponent 的新实例添加到列表中,保持状态同步。
尝试一下,让我知道它是否适合你:)
import React, { useState, useEffect } from 'react';
interface Option {
id: number;
name: string;
}
const options: Option[] = [
{ id: 1, name: 'Test 1' },
{ id: 2, name: 'Test 2' },
{ id: 3, name: 'Test 3' },
];
const TestComponent = ({ index, selectedOption, handleSelectChange }: { index: number; selectedOption: Option; handleSelectChange: (index: number, id: number) => void }) => {
return (
<div className="flex flex-row gap-5" id={`${index}`}>
<select
value={selectedOption.id}
onChange={(e) => handleSelectChange(index, parseInt(e.target.value))}
>
{options.map((option, idx) => (
<option key={idx} value={option.id}>
{option.name}
</option>
))}
</select>
<input readOnly value={selectedOption.id} />
</div>
);
};
export default function CreateTestComponent() {
const [selectedOptions, setSelectedOptions] = useState<Option[]>([options[0]]);
const [components, setComponents] = useState<JSX.Element[]>([]);
useEffect(() => {
setComponents([<TestComponent key={0} index={0} selectedOption={options[0]} handleSelectChange={handleSelectChange} />]);
}, []);
const handleSelectChange = (index: number, id: number) => {
setSelectedOptions((old) =>
old.map((opt, idx) => (idx === index ? options.find((option) => option.id === id)! : opt))
);
};
const addComponent = () => {
setSelectedOptions([...selectedOptions, options[0]]);
setComponents((old) => [
...old,
<TestComponent key={old.length} index={old.length} selectedOption={options[0]} handleSelectChange={handleSelectChange} />,
]);
};
return (
<>
<button type="button" onClick={addComponent} className="bg-black text-white rounded px-3 py-1">
Add a Component
</button>
<div>
<h1>Component testing!</h1>
<div>
<ul className="list-none">
{components.map((component, index) => (
<li key={index}>
<div className="flex flex-row gap-5">{component}</div>
</li>
))}
</ul>
</div>
</div>
</>
);
}