我已附上一个简化的示例来演示我的问题:
https://codesandbox.io/s/reactusehook-stack-issue-piq15
我有一个接收配置的父组件,应该渲染其屏幕。渲染的屏幕应该可以控制父级外观。在上面的示例中,我用颜色进行了演示。但是实际的用例是流屏幕,该屏幕具有可以由孩子控制的下一步和后退按钮。
在示例中,我为屏幕定义了常见的道具:
type GenericScreenProps = {
setColor: (color: string) => void;
};
我创建了第一个屏幕,该屏幕不关心颜色(父级应该默认)
const ScreenA = (props: GenericScreenProps) => {
return <div>screen A</div>;
};
我创建了第二个屏幕,该屏幕在安装时明确定义了颜色
const ScreenB = ({ setColor }: GenericScreenProps) => {
React.useEffect(() => {
console.log("child");
setColor("green");
}, [setColor]);
return <div>screen B</div>;
};
我创建了一个地图,以便能够通过索引引用组件
const map: Record<string, React.JSXElementConstructor<GenericScreenProps>> = {
0: ScreenA,
1: ScreenB
};
最后,我创建了父级,该父级具有一个按钮,该按钮可交换组件并在组件更改时设置默认值
const App = () => {
const [screenId, setScreenId] = useState(0);
const ComponentToRender = map[screenId];
const [color, setColor] = useState("red");
React.useEffect(() => {
console.log("parent");
setColor("red"); // default when not set should be red
}, [screenId]);
const onButtonClick = () => setScreenId((screenId + 1) % Object.keys(map).length)
return (
<div>
<button style={{ color }} onClick={onButtonClick}>
Button
</button>
<ComponentToRender setColor={setColor} />
</div>
);
};
在此示例中,屏幕A的默认颜色应为red
,第二个屏幕的默认颜色应为green
。
但是,颜色保留为red
,因为useEffect
正在使用堆栈来执行代码。如果您运行代码,则在单击按钮后将看到child
,然后是parent
。
我已经考虑了以下解决方案,但它们并不理想:
这可能是一个反模式,子组件控制其父组件的行为,因为如果不为每个屏幕复制父容器,我将无法确定这样做的方式。我想要保留单亲的原因是要在屏幕之间启用过渡。
答案 0 :(得分:0)
如果我对问题的理解正确,则无需将setColor
传递给孩子们。使表达式更明确可能会使代码更长一些,但我认为它有助于提高可读性。由于您分享的只是一个简化的示例,请让我知道它是否适合您的实际情况:
const ScreenA = () => {
return <div>screen A</div>;
};
const ScreenB = () => {
return <div>screen B</div>;
};
const App = () => {
const [screen, setScreen] = useState<"a" | "b">("a");
const [color, setColor] = useState<"red" | "green">("red");
const onButtonClick = () => {
if (screen === "a") {
setScreen("b");
setColor("green");
} else {
setScreen("a");
setColor("red");
}
};
return (
<div>
<button style={{ color }} onClick={onButtonClick}>
Button
</button>
{screen === "a" ? <ScreenA /> : <ScreenB />}
</div>
);
};
render(<App />, document.getElementById("root"));