我想将一些自定义事件从父React组件转发到一些子组件,以便它们可以根据收到的事件做出决定,而无需父组件直接管理子组件的状态。 这是一个简化的示例,用于展示我正在尝试的想法。
家长控制.tsx
export default function ParentControl() {
const divRef = useRef<HTMLDivElement>(null);
const dispatchEvent = (e: CustomEvent) => {
divRef.current.dispatchEvent(e);
};
return (
<div ref={divRef} >
<Child name="Child1" dispatcher={divRef} />
<Child name="Child2" dispatcher={divRef} />
<button onClick={() => dispatchEvent(new CustomEvent('prev', {detail: ''})) }>
Prev
</button>
<button onClick={() => dispatchEvent(new CustomEvent('next', {detail: ''})) }>
Next
</button>
</div>
);
}
Child.tsx
export type ChildProps = {
name: string;
dispatcher: RefObject<HTMLElement>;
};
export default function Child({ name, dispatcher }: ChildProps) {
const [index, setIndex] = useState(0);
const selectPrev = (event: CustomEvent) => {
console.log(`Received event: ${event.type}, index: ${index}`);
setIndex(index - 1);
};
const selectNext = (event: CustomEvent) => {
console.log(`Received event: ${event.type}, index: ${index}`);
setIndex(index + 1);
};
const printState = () => {
console.log(`printState(): index: ${index}`);
};
useEffect(() => {
const dispatch = dispatcher.current;
dispatch.addEventListener("next", selectNext);
dispatch.addEventListener("prev", selectPrev);
return () => {
dispatch.removeEventListener("next", selectNext);
dispatch.removeEventListener("prev", selectPrev);
};
}, []);
return (
<div>
<div>
{name} Index: {index}
</div>
<button onClick={() => printState()}>Print</button>
</div>
);
}
运行这个我看到
selectNext()
和 selectPrev()
方法确实在父组件调度事件时被调用,但是这些方法只能看到子组件中的初始索引状态 = 0,因此它们只能将索引更改为-1或1。这些回调如何看到孩子当前的真实状态?
是否可以使用事件(稍后可能包括多种类型)以某种方式完成此任务,而不必使用 React 的 Context API?
您的
useEffect
deps 是一个空数组 ([]
)。如果变量在效果内部被引用并且未包含在 deps 数组中(selectNext
和 selectPrev
),通常表明存在问题。这是因为效果内部使用的任何未在 deps 中引用的闭包都可能是过时的并保存过时的值。
您的代码可以通过将它们添加到
useEffect
部门来修复。请注意,由于它们会在每次渲染时发生变化(导致循环),因此我还将这些方法包装在 useCallback
中,并将 index
放入 deps 数组中,以便在索引更改时刷新闭包。这反过来又会刷新效果。
const selectPrev = useCallback(
(event: CustomEvent) => {
console.log(`Received event: ${event.type}, index: ${index}`);
setIndex(index - 1);
},
[index]
);
const selectNext = useCallback(
(event: CustomEvent) => {
console.log(`Received event: ${event.type}, index: ${index}`);
setIndex(index + 1);
},
[index]
);
const printState = () => {
console.log(`printState(): index: ${index}`);
};
useEffect(() => {
const dispatch = dispatcher.current;
dispatch.addEventListener("next", selectNext);
dispatch.addEventListener("prev", selectPrev);
return () => {
dispatch.removeEventListener("next", selectNext);
dispatch.removeEventListener("prev", selectPrev);
};
}, [selectPrev, selectNext]);
但是我必须强调,你所做的事情在 React 中被认为是一种非常奇怪的模式,主要有两个原因:
useImperativeHandle
可能会更好地满足你想要做的事情。