I'm brand new to the useContext
hook and trying to figure out what I'm doing wrong... without the top imports, here's what my code looks like:
So the root of my project looks like this:
export default function App() {
return (
<div className="App">
<AppProvider>
<Header/>
<Toolbar/>
<Canvas/>
<Imports/>
<Footer/>
</AppProvider>
</div>
);
}
Then I have a separate file called AppProvider that has this:
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>
)
}
I don't have any problems passing the state
object and the addCanvasItems
function down to the first level of children (so Header, Toolbar, Canvas, etc.) but I have children inside those components where I can't access the values.
For clarity - inside the Canvas
component, I have a Stage
component, inside that there's a Layer
component, inside that there's an Items
component (I'm using Konva and need to have all this nesting).
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>
)
}
I need to access the state
object in the Items
component but I want to avoid prop drilling. For testing, I created a useEffect
function to print the state every time it changes. The Items
component prints undefined
on the first load, whereas the Canvas
component prints the full object.
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)
What am I missing with this hook and how to implement it??
MANY thanks for the help!! 🙏🙏🙏🙏
If you look at the return value of Stage
it does not render children
, meaning Layer and Items are being treated as special cases. I think you're best bet is to pass props to them instead of relying on context since they appear to technically not be part of React's rendering context at all, but part of Konva's custom renderer: 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}
/>
);
}
There's an explanation in the docs about how to do this properly:
https://github.com/konvajs/react-konva#usage-with-react-context
Due to a known issue with React, Contexts are not accessible by children of the react-konva Stage component. If you need to subscribe to a context from within the Stage, you need to "bridge" the context by creating a Provider as a child of the Stage. For more info, see this discussion and this react-redux demo. Here is an example of bridging the context (live demo):
Try adding your providers again as children of Stage:
<Stage
container='canvas'
onDrop={onDrop}
onDragOver={onDragOver}
>
<CanvasItems.Provider value={state}>
<UpdateCanvasItems.Provider value={addCanvasItem}>
<Layer>
<Items />
</Layer>
</UpdateCanvasItems.Provider>
</CanvasItems.Provider>
</Stage>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With