gpt4 book ai didi

reactjs - useState 钩子(Hook)一次只能设置一个对象,并将同一个数组的另一个对象返回到初始状态

转载 作者:行者123 更新时间:2023-12-03 17:18:53 27 4
gpt4 key购买 nike

我有这种形式的数据:

books = [{
id: 1,
name: "book-1",
audiences: [
{
audienceId: 1,
name: "Cat A",
critics: [
{ id: 5, name: "Jack" },
{ id: 45, name: "Mike" },
{ id: 32, name: "Amanda" }
],
readers: [
{ id: 11, fullName: "Mike Ross" },
{ id: 76, fullName: "Natalie J" },
{ id: 34, fullName: "Harvey Spectre" }
]
}]
表格是嵌套的。每本书都有评论家和读者,会根据 AudienceId 的值来渲染每个表单
<div className="App">
{books.map((book, index) => (
<div key={book.id}>
<h1>Books {index + 1}</h1>
{book.audiences.map((au) => (
<div key={au.audienceId}>
<h3>{au.name}</h3>
<Recap
audiences={au}
shouldRenderCritics={au.audienceId === 2}
shouldRenderReaders={au.audienceId === 1}
criticsChildren={renderByAudience(au.audienceId)}
readersChildren={renderByAudience(au.audienceId)}
/>
</div>
))}
</div>
))}
</div>
这是根据 AudienceId 呈现的表单
return (
<div className="root">
<div>
{props.audienceId === 1 && (
<A
setReader={setReader(readerIdx)}
reader={selectedReader as IreaderState}
/>
)}
</div>
<div>
{props.audienceId === 2 && (
<B
setCritic={setCritic(criticIdx)}
critic={selectedCritic as IcriticState}
/>
)}
</div>
</div>
);
最后,对于每个读者/评论家,都有一个输入表格。
export default function A(props: Aprops) {
const handleChange = (
event: ChangeEvent<{ value: number | string; name: string }>
) => {
const { name, value } = event.target;

const r = { ...props.reader };
r[name] = value;
props.setReader(r);
};

return (
<div>
<TextField
name="rate"
type="number"
value={get(props.reader, "rate", "")}
onChange={handleChange}
/>
<TextField
name="feedback"
value={get(props.reader, "feedback", "")}
onChange={handleChange}
/>
</div>
);
问题
每次我填写评论家/读者字段时,它都会为该对象设置状态,但也会为其余对象设置初始状态。它只设置焦点所在的表单状态,不保留其他表单的值
我不知道是什么导致了问题。
这是一个重现问题 https://codesandbox.io/s/project-u0m5n 的沙盒

最佳答案

哇,这太棒了。最终问题源于您调用以下组件的事实:

export default function FormsByAudience(props: FormsByAudienceProps) {
const [critics, setCritics] = useCritics(props.books);

const setCritic = (index: number) => (critic: IcriticState) => {
const c = [...critics];

c[index] = critic;
setCritics(c);
};

// ...
// in render method:
setCritic={setCritic(criticIdx)}
对于每一个不同的评论家(和读者):
props.audiences.critics.map((c) => (
<div key={c.id}>
<div>{c.name}</div>
{props.shouldRenderCritics &&
props.criticsChildren &&
cloneElement(props.criticsChildren, { criticId: c.id })}
</div>
))}
在哪里 props.criticsChildren包含 <FormsByAudience audienceId={id} books={books} /> .
结果,在单个渲染中, critics 有许多单独的绑定(bind)。多变的。你没有一个 critics对于整个应用程序或给定的受众;你有多个 critics数组,每个评论家一个。为一位评论家的 critics 设置状态 FormsByAudience 中的数组不会导致对其他 critics 的更改其他评论家的 React 元素关闭的数组。
修复它,假设 critics正在从 props.books 创建数组, critics state 应该放置在使用书籍的同一级别附近,并且绝对不在嵌套映射器函数的组件内。然后将状态和状态 setter 传递给 child 。
完全相同的事情适用于 readers状态。
这是说明此问题的最小实时堆栈片段:

const people = ['bob', 'joe'];
const Person = ({name, i}) => {
const [peopleData, setPeopleData] = React.useState(people.map(() => 0));
console.log(peopleData.join(','));
const onChange = e => {
setPeopleData(
peopleData.map((num, j) => j === i ? e.target.value : num)
)
};
return (
<div>
<div>{name}</div>
<input type="number" value={peopleData[i]} onChange={onChange} />
</div>
);
};
const App = () => {
return people.map(
(name, i) => <Person key={i} {...{ name, i }} />
);
};

ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

为了使 props 的传递更易于键入和阅读,您可以考虑将 4 个变量(2 个数据和 2 个 setter)合并到一个对象中。
export default function App() {
const [critics, setCritics] = useCritics(books);
const [readers, setReaders] = useReaders(books);
const dataAndSetters = { critics, setCritics, readers, setReaders };
const renderByAudience = (id: number) => {
return <FormsByAudience audienceId={id} {...{ books, dataAndSetters }} />;
};
dataAndSetters直到你到达
export default function FormsByAudience(props: FormsByAudienceProps) {
const { critics, setCritics, readers, setReaders } = props.dataAndSetters;
现场演示:
https://codesandbox.io/s/project-forked-woqhf

如果我可以提供一些其他建议,以显着提高您的代码质量:
  • 更改Recap这样FormsByAudience导入并调用 Recap而不是在 parent 中。 renderByAudience 真的很奇怪(而且一目了然很难理解)创建一个有时永远不会被使用的 React 元素。
  • Recap评论家和读者有没有调换。这可能是无意的。这:
    <h5>Readers</h5>
    {!isEmpty(props.audiences.critics) &&
    props.audiences.critics.map((c) => (
    应该是
    <h5>Readers</h5>
    {props.audiences.readers.map((c) => (
    (在映射数组之前不需要 isEmpty 检查)
  • 整体
    const selectedReader =
    readers.find((r) => r.readerId === (props.readerId as number)) || {};
    const readerIdx = readers.indexOf(selectedReader as IreaderState);
    const selectedCritic =
    critics.find((r) => r.criticId === (props.criticId as number)) || {};
    const criticIdx = critics.indexOf(selectedCritic as IcriticState);
    是不必要的。只需从父组件传递阅读器/评论家索引,然后使用 critics[criticIdx]找到有问题的评论家。
  • hook.ts , 这:
    const byAudience = books
    .map((b) => b.audiences)
    .flat()
    .filter((bk) => [1].includes(bk.audienceId));
    简化为
    const byAudience = books
    .flatMap(b => b.audiences)
    .filter(bk => bk.audienceId === 1);
    还有这个:
    byAudience.forEach((el) => {
    el.readers.map((r) => {
    return readersToSet.push({ feedback: "", rate: "", readerId: r.id });
    });
    return readersToSet;
    });
    没有意义,因为 forEach忽略它的返回值,.map仅应在从回调返回值以构造新数组时使用。使用 flatMapbyAudience , 或插入 for..of环形。
    const readersToSet = byAudience.flatMap(
    el => el.readers.map(
    r => ({ feedback: "", rate: "", readerId: r.id })
    )
    );
  • 关于reactjs - useState 钩子(Hook)一次只能设置一个对象,并将同一个数组的另一个对象返回到初始状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66509491/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com