reactjs - 带有数组道具的自定义钩子正在无限循环
问题描述
我知道这里有很多类似的问题,但我无法找到让我意识到我在这里做错了什么的答案。
我制作了一个名为的自定义钩子,如果当前状态有百万作为乘数useAmounts
,它可以将文本5%
转换为0.05
或500
转换为。500,000,000
我需要这个用于各种组件中的多个输入。
我还制作了另一个名为的钩子useMerged
,它基本上有一个输入数据数组,例如"5%", "200 million"
加上一些额外的数据。然后该钩子应将解析后的数字合并到所需的对象中。
最后,我想要一个对象列表,我可以在其中检索值和函数以更新列表中的值。但我得到了无限循环。
我试图做一个最小的例子,但是对于我正在寻找的模式来说,很难让它完全最小化。任何帮助深表感谢!我的猜测是有一个更好的模式来实现我在这里尝试的。
import * as React from "react";
interface Amount {
multiplier: number;
displayText: string;
value: number;
updateAmount: (text: string) => void;
}
function useAmounts(initialValues: string[]): Amount[] {
const [amounts, setAmounts] = React.useState<
{
multiplier: number;
displayText: string;
value: number;
}[]
>([]);
React.useEffect(() => {
console.log(
`useAmounts: Setting initial amounts of length ${initialValues.length}`
);
setAmounts(
initialValues.map(text => {
const matches = text.match(/\d+/g);
const multiplier = text.includes("%") ? 0.01 : 1;
const displayText = matches ? matches[0] : "";
return {
multiplier,
displayText,
value: parseFloat(displayText) * multiplier
};
})
);
}, [initialValues]);
const updateAmount = (index: number) => (text: string) => {
const matches = text.match(/\d+/g);
setAmounts(prevState => {
const newState = [...prevState];
newState[index].displayText = matches ? matches[0] : "";
return newState;
});
};
return amounts.map((a, index) => {
return {
multiplier: a.multiplier,
displayText: a.displayText,
value: a.value,
updateAmount: updateAmount(index)
};
});
}
interface State {
text: string;
additionalValue: string;
}
interface MergedState {
amount: {
multiplier: number;
displayText: string;
value: number;
updateAmount: (text: string) => void;
};
additionalValue: string;
}
function useMerged(states: State[]) {
const [initialText, setInitialText] = React.useState<string[]>([]);
const amounts = useAmounts(initialText);
const [mergedState, setMergedState] = React.useState<MergedState[]>([]);
React.useEffect(() => {
console.log(
`useMerged: setting initial input for useAmounts in useMerged of length ${
states.length
}`
);
setInitialText(states.map(v => v.text));
}, [states]);
React.useEffect(() => {
if (amounts.length === states.length)
setMergedState(
states.map((s, index) => {
return {
additionalValue: s.additionalValue,
amount: amounts[index]
};
})
);
}, [amounts.length, states.length]);
return mergedState;
}
const delayedInitialState: State[] = [
{ text: "100 million", additionalValue: "A" },
{ text: "25%", additionalValue: "B" }
];
export default function App() {
const [state, setState] = React.useState<State[]>([]);
const mergedStates = useMerged(state);
React.useEffect(() => {
console.log("App: setting initial state on app level");
setState(delayedInitialState);
}, []);
return (
<div className="App">
<ul>
{mergedStates.map((a, index) => (
<li key={index}>
<input
value={a.amount.displayText}
onChange={event => a.amount.updateAmount(event.target.value)}
/>
{a.amount.multiplier}
</li>
))}
</ul>
</div>
);
}
编辑
我也对这里的生命周期感到困惑。控制台输出:
- useAmounts:设置长度为 0 的初始数量
- useMerged: 为 useMerged 中长度为 0 的 useAmounts 设置初始输入
- 应用程序:在应用程序级别设置初始状态
- useAmounts:设置长度为 0 的初始数量
- useMerged: 为 useMerged 中长度为 2 的 useAmounts 设置初始输入
- useAmounts:设置长度为 2 的初始数量
为什么之前useAmounts
的渲染改变了初始输入?App
useMerged
解决方案
在你的useMerged
函数中,amounts
每次你做的时候都会有不同的参考const amounts = useAmounts(states.map(v => v.text));
。这意味着当您签入useEffect
以查看initialValues
您的函数是否已更改时useAmounts
,它将始终将新引用视为更改。
解决此问题的一种方法-您可以改为useAmounts(states)
将代码更新为 aState[]
而不是 a string[]
,而不是调用map
它吗?
推荐阅读
- c++ - 在 C++ 中的 Qt 中使用 WINAPI 线程的问题
- javascript - 如何使用按钮从剪贴板粘贴图像?
- java - 在脚本中,如何测试是使用 Corretto 还是 Oracle JRE?
- python - pandas groupby,上下组成员的区别
- javascript - 自动调整文本区域大小
- regex - 每当 URL 中有大写字母时反应路由器重定向
- flutter - 如何获得颤动的future.wait响应?
- java - 在地图中检索数据
- spring - 我可以根据包注入bean吗?
- arangodb - ArangoDB 无法初始化 RocksDB 引擎:IO 错误:同时锁定文件