我是useContext
钩子上的新手,试图弄清楚我做错了什么...如果没有顶级导入,这就是我的代码:
所以我项目的根目录如下:
export default function App() {
return (
<div className="App">
<AppProvider>
<Header/>
<Toolbar/>
<Canvas/>
<Imports/>
<Footer/>
</AppProvider>
</div>
);
}
然后我有一个名为AppProvider的单独文件,该文件具有以下内容:
const CanvasItems = createContext();
const UpdateCanvasItems = createContext();
export function useCanvasItems() {
return useContext(CanvasItems)
}
export function useChangeItems() {
return useContext(UpdateCanvasItems)
}
export default function AppProvider({children}) {
const [state, setState] = useState({
imports: [],
images: [],
text: [],
circles: [],
rectangles: [],
draw: [],
})
// function pushes new items in to state object
function addCanvasItem(type, source) {
if (type === 'import') {
const tempState = [...state.imports];
tempState.push({id: Date.now(), image: source});
setState({...state, imports: tempState})
} else if (type === 'image') {
const tempState = [...state.images];
tempState.push({
id: Date.now(),
src: source.src,
x: window.innerWidth * Math.random(),
y: window.innerHeight * Math.random(),
})
setState({...state, images: tempState})
}
console.log("state after new item added:", state)
}
return (
<CanvasItems.Provider value={state}>
<UpdateCanvasItems.Provider value={addCanvasItem}>
{children}
</UpdateCanvasItems.Provider>
</CanvasItems.Provider>
)
}
将state
对象和addCanvasItems
函数传递到第一级子级(例如Header,Toolbar,Canvas等)时,我没有任何问题,但是在这些子级中有子级无法访问值的组件。
为清楚起见-在Canvas
组件中,我有一个Stage
组件,在其中有一个Layer
组件,在其中有一个Items
组件(我是使用Konva,并且需要进行所有嵌套)。
export default function Canvas() {
const { onDrop, onDragOver } = useDragDrop;
const state = useCanvasItems();
useEffect(() => {
console.log("state in canvas", state)
}, [state])
return (
<div id='canvas'>
<Stage
container='canvas'
onDrop={onDrop}
onDragOver={onDragOver}
>
<Layer>
<Items/>
</Layer>
</Stage>
</div>
)
}
我需要访问state
组件中的Items
对象,但是我想避免道具钻探。为了进行测试,我创建了一个useEffect
函数来在每次更改状态时打印状态。 Items
组件在第一次加载时打印undefined
,而Canvas
组件则打印整个对象。
export default function Items() {
const state = useCanvasItems();
useEffect(() => {
console.log("state in items", state)
}, [state])
return (
<>
{state && state.images.map(image => {
return <NewImage image={image} />
})}
</>
)
};
what prints to the console on load and after a file is imported (changing the state)
这个钩子我缺少什么以及如何实现?
非常感谢您的帮助!! ????
答案 0 :(得分:2)
如果您查看Stage
的返回值,它不会呈现children
,这意味着“图层”和“项目”被视为特殊情况。我认为您最好的选择是将道具传递给他们,而不是依赖于上下文,因为从技术上讲,它们似乎根本不是React渲染上下文的一部分,而是Konva自定义渲染器的一部分:KonvaRenderer
https://github.com/konvajs/react-konva/blob/master/src/ReactKonvaCore.js#L89
// Stage component
render() {
const props = this.props;
return (
<div
ref={(ref) => (this._tagRef = ref)}
accessKey={props.accessKey}
className={props.className}
role={props.role}
style={props.style}
tabIndex={props.tabIndex}
title={props.title}
/>
);
}
文档中有关于如何正确执行此操作的说明:
https://github.com/konvajs/react-konva#usage-with-react-context
由于React的已知问题,无法通过react-konva Stage组件的子级访问上下文。如果需要从舞台内预订上下文,则需要通过将提供者创建为舞台的子级来“桥接”上下文。有关更多信息,请参见此讨论和此react-redux演示。这是桥接上下文的示例(实时演示):
尝试再次将提供者添加为Stage的子代:
<Stage
container='canvas'
onDrop={onDrop}
onDragOver={onDragOver}
>
<CanvasItems.Provider value={state}>
<UpdateCanvasItems.Provider value={addCanvasItem}>
<Layer>
<Items />
</Layer>
</UpdateCanvasItems.Provider>
</CanvasItems.Provider>
</Stage>