Состояние становится неопределенным после отправки с помощью UseReducer и UseContext

Цель: я пытаюсь переключить видимость 3D-модели с помощью редуктора и контекста React. 3D-рендеринг выполняется с помощью версии фреймворка React hooks под названием Xeokit. Модели визуализируются в элемент холста (3DCanvas.js), передавая их в качестве реквизита элементу Xeokit Viewer (XKTModel.js).

Проблема в том, что после отправки отправки состояние становится неопределенным. Контекст работает только со значением по умолчанию, заданным редюсеру. Кажется, не имеет значения, какой компонент отправляет отправку.

Я записал в консоль состояние для каждого компонента, используя хук UseContext, чтобы увидеть, что происходит. То же самое и с редуктором.

Я много искал, но не смог найти решение аналогичной проблемы. Может ли кто-нибудь более опытный заметить мою ошибку?

Создание контекста:

import React from "react";

const ViewControlsContext = React.createContext()

export {ViewControlsContext as default}

Редуктор:

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}

Приложение:

import React from 'react';

import './App.scss'

import ThreeDeeCanvas from './Components/3DCanvas'

function App(){
  return (<div>
    <ThreeDeeCanvas />
  </div>)
};

export default App;

3DCanvas (компонент, в который рендерятся модели, обеспечивает контекст):

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 (исходник и реквизит для 3D-моделей, использует контекст):

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 (контейнер для отдельных 3D-элементов управления):

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 (отдельные переключатели для изменения реквизитов 3D-вида, отправка отправки):

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}

Оператор запятой возвращает последнее значение. Если у вас есть return foo, console.log, он возвращает undefined, так как console.log возвращает undefined в вашем редукторе.

Wiktor Zychla 22.04.2022 20:00
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Сравнение структур данных: Массивы и объекты в Javascript
Сравнение структур данных: Массивы и объекты в Javascript
Итак, вы изучили основы JavaScript и хотите перейти к изучению структур данных. Мотивация для изучения/понимания Структур данных может быть разной,...
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Создание собственной системы электронной коммерции на базе Keystone.js - настройка среды и базовые модели
Прошлая статья была первой из цикла статей о создании системы электронной коммерции с использованием Keystone.js, и она была посвящена главным образом...
Приложение для отслеживания бюджета на React js для начинающих
Приложение для отслеживания бюджета на React js для начинающих
Обучение на практике - это проверенная тема для достижения успеха в любой области. Если вы знаете контекст фразы "Практика делает человека...
Стоит ли использовать React в 2022 году?
Стоит ли использовать React в 2022 году?
В 2022 году мы все слышим о трендах фронтенда (React, Vue), но мы не знаем, почему мы должны использовать эти фреймворки, когда их использовать, а...
1
1
32
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы возвращаете результат оператора запятой, то есть результат console.log, который является недействительным.

Оператор запятой

The comma operator (,) evaluates each of its operands (from left to right) and returns the value of the last operand. This lets you create a compound expression in which multiple expressions are evaluated, with the compound expression's final value being the value of the rightmost of its member expressions.

Решение

Регистрируйте состояние и действие независимо от возврата функции редуктора.

const ViewControlsReducer = (state, action) => {
  switch (action.type) {
    case 'POPULATE_STATE':
      console.log("populate dispatch: ", { action, state });
      return action.state;

    case 'TOGGLE_SITE_VISIBILITY':
      console.log("toggle dispatch: ", { action, state });
      return {
        ...state,
        site_visibility: !state.site_visibility
      };

    default:
      console.log("default: ", { action, state });
      return state;
  }
}

Другие вопросы по теме