我知道这里有很多类似的问题,但是我找不到能够使我意识到我在做什么错的答案。
我制作了一个名为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
在App
之后之前 useMerged
进行了渲染?
答案 0 :(得分:1)
在您的useMerged
函数中,每次执行amounts
时,const amounts = useAmounts(states.map(v => v.text));
将是一个不同的引用。这就是说,当您签入useEffect
来查看initialValues
函数中的useAmounts
是否已更改时,它将始终将新引用视为更改。
解决此问题的一种方法-您是否可以只useAmounts(states)
并将代码更新为State[]
而不是string[]
,而不是调用map
?