Goal: I am trying to toggle the visibility of a 3D model using React reducer and context. 3D rendering is done with React hooks version of a framework called Xeokit. Models are rendered into a canvas element (3DCanvas.js) by giving them as props to a Xeokit Viewer element (XKTModel.js).
The problem is, once a dispatch is sent, the state becomes undefined. The context works only with the default value given to the reducer. Doesn't seem to matter which component sends the dispatch.
I have written into console the state for each component using the UseContext hook to see what is going on. Same thing with the reducer.
I did a lot of searching but could not find a similar issue being addressed. Can someone more experienced spot the mistake I have made?
Context creation:
import React from "react";
const ViewControlsContext = React.createContext()
export {ViewControlsContext as default}
Reducer:
const ViewControlsReducer = (state, action) => {
switch (action.type) {
case 'POPULATE_STATE':
return action.state, console.log("populate dispatch: ", action, state)
case 'TOGGLE_SITE_VISIBILITY':
return {
...state,
//site_visibility: !state.site_visibility
//^commented out, as will cause an error because state undefined
}, console.log("toggle dispatch: ", action, state)
default:
return state, console.log("default: ", action, state)
}
}
export {ViewControlsReducer as default}
App:
import React from 'react';
import './App.scss'
import ThreeDeeCanvas from './Components/3DCanvas'
function App(){
return (<div>
<ThreeDeeCanvas />
</div>)
};
export default App;
3DCanvas (component into which models are rendered, provides Context):
import React, { useEffect, useReducer } from 'react'
import { Container, Col, Row } from 'react-bootstrap'
import XKTModel from './XKTModel';
import ThreeDeeControls from './3DControls';
import LogoCanvas from './LogoCanvas'
import ViewControlsContext from '../Context/ViewControlsContext';
import ViewControlsReducer from '../Reducers/ViewControlsReducer';
const ThreeDeeCanvas = () => {
const def = {
site_visibility: false
}
const [viewControls, dispatch] = useReducer(ViewControlsReducer, def)
useEffect(() => {
dispatch({type: 'POPULATE_STATE', state: def})
dispatch({type: 'POPULATE_STATE', state: def}) //test, with this state is undefined
}, [])
console.log("3dcanvas: ", viewControls)
return (
<div>
<LogoCanvas/>
<Col className='ThreeDeeWindow'>
<ViewControlsContext.Provider value={{viewControls, dispatch}} >
<XKTModel/>
<ThreeDeeControls/>
</ViewControlsContext.Provider>
</Col>
</div>
)
}
export {ThreeDeeCanvas as default}
XKTModel (source and props for 3D models, uses Context):
import React, { useContext } from 'react';
import { navCubeSettings } from '../models';
import { XKTViewer } from 'xeokit-react';
import ViewControlsContext from '../Context/ViewControlsContext';
import { IFCObjectDefaultColors } from '../default_colors';
const mainModel = {
id: 'main_model',
src: './xkt/Showcase.xkt',
metaModelSrc: './xkt/Showcase.json',
edges: true,
objectDefaults: IFCObjectDefaultColors,
}
const siteModel = {
id: 'site_model',
src: './xkt/ShowcaseSite.xkt',
metaModelSrc: './xkt/ShowcaseSite.json',
edges: true
}
const XKTModel = () => {
const {viewControls} = useContext(ViewControlsContext)
console.log("XKTModel: ", viewControls)
//siteModel.visible = viewControls.site_visibility
//^commented out, as will cause an error because state undefined
return (
<XKTViewer
canvasID="modelCanvas"
width={800}
height={800}
models={[mainModel, siteModel]}
navCubeSettings={navCubeSettings}
/>
)
};
export default XKTModel;
3DControls (Container for individual 3D controls):
import React, { useContext } from 'react'
import { Container, Col, Row } from 'react-bootstrap'
import ThreeDeeControl from './3DControl'
import ViewControlsContext from '../Context/ViewControlsContext'
const ThreeDeeControls = () => {
const {viewControls} = useContext(ViewControlsContext)
console.log("3dcontrols: ", viewControls)
return (
<Container className='ThreeDeeControls p-0 d-flex justify-content-between'>
<ThreeDeeControl id='site' label='Show site'/>
</Container>
)
}
export {ThreeDeeControls as default}
3DControl (individual toggles for changing 3D view props, sends dispatch):
import React, { useState, useContext, useReducer } from 'react'
import { Container, Form } from 'react-bootstrap'
import ViewControlsContext from '../Context/ViewControlsContext'
const ThreeDeeControl = ({id, label}) => {
const {viewControls, dispatch} = useContext(ViewControlsContext)
console.log("3dsinglecontrol: ", viewControls)
const [isSwitchOn, setIsSwitchOn] = useState(false);
const onSwitchAction = () => {
setIsSwitchOn(!isSwitchOn);
dispatch({type: 'TOGGLE_SITE_VISIBILITY'})
}
return (
<Form className='ThreeDeeControl'>
<Form.Check
type="switch"
id={id}
label={label}
checked={isSwitchOn}
onChange={onSwitchAction}
/>
</Form>
)
}
export {ThreeDeeControl as default}