Skip to content
This repository has been archived by the owner on Jan 27, 2021. It is now read-only.

growing number of reducer function calls ending with error #27

Closed
AlSharp opened this issue May 25, 2019 · 3 comments
Closed

growing number of reducer function calls ending with error #27

AlSharp opened this issue May 25, 2019 · 3 comments
Labels
investigation required We're not sure what's going on yet

Comments

@AlSharp
Copy link

AlSharp commented May 25, 2019

Is it a bug, feature request or question?

the number of reducer function calls is growing on each dynamic reducer mounting

Which package(s) does this involve?

I am using only
"@redux-dynostore/core": "^2.0.0"
"redux-subspace": "^3.0.1"

my dependencies:

  "dependencies": {
    "@redux-dynostore/core": "^2.0.0",
    "classnames": "^2.2.6",
    "react": "^16.3.1",
    "react-bootstrap": "^0.32.1",
    "react-color": "^2.14.1",
    "react-dom": "^16.3.1",
    "react-redux": "^5.0.7",
    "react-scripts": "1.1.4",
    "redux": "^3.0.0",
    "redux-form": "^7.4.2",
    "redux-subspace": "^3.0.1",
    "redux-thunk": "^2.3.0",
    "resize-sensor": "0.0.6",
    "socket.io-client": "^2.1.0",
    "styled-components": "^4.2.0"
  },

Input Code

I have a backend where I react when I attach or detach usb device by sending message to react app.

here is the block of code where I handle the attaching or detaching usb devices

let robots = {}; \\ subspaced stored here
socket.on('hardwareEvents', data => {
  switch(data.todo) {
    case 'usbEvent': {
      const { addedRobots, removedRobots } = data.value;

      console.log('addedRobots: ', addedRobots);
      console.log('removedRobots: ', removedRobots);
      removedRobots.forEach(port => {
        unregister(store, port);
        robots = removeProperties(robots, [`${port}`, `${port}Axes`]);
      });
      
      addedRobots.forEach(robot => {
        robots = {...robots, ...register(store, robot)};
      })

      store.dispatch(
        {
          type: 'HANDLE_USB_PORTS_EVENTS',
          payload: addedRobots
        }
      );

      store.dispatch(
        {
          // ROBOTS OR ANY OTHER DEVICES
          type: 'HANDLE_ROBOTS_INIT_STATE_GET',
          payload: addedRobots.map(robot => ({port: robot.port, axes: robot.axes}))
        }
      )

      break;
    }
    default: return;
  }
});

This is my reducer

import { combineReducers } from 'redux';
import axisReducer from './axis';
import { subspace, namespaced } from 'redux-subspace';

export const register = (store, robot) => {
  const {port, axes} = robot;
  const status = (state={
    type: robot.type,
    allAxesEnabled: false,
    baudRate: '9600',
    running: false,
  }, action) => {

    switch(action.type) {
      case 'HANDLE_BAUD_RATE_UPDATE':
        return { ...state, baudRate: action.payload };
      case 'HANDLE_TURN_ON_OR_OFF':
        return { ...state, allAxesEnabled: action.payload };
      case 'HANDLE_COMMUNICATION_RESET': {
        return { ...state, baudRate: action.payload }
      }
      case 'HANDLE_DISCONNECTION_EVENT':
        return { ...state, allAxesEnabled: false, running: false, baudRate: '9600' };
      default: {
        console.log('robot default');
        return state;
      }
    }
  }

  const axisReducers = axes.reduce(
    (o, key) => (
      {
        ...o,
        [`axis${key}`]: namespaced(`axis${key}`)(axisReducer)
      }
    ), {}
  )

  const robotReducer = combineReducers({
    status: namespaced('STATUS')(status),
    ...axisReducers
  });

  store.attachReducers(
    {
      [port]: namespaced(port)(robotReducer)
    }
  )

  const robotStore = subspace(state => state[port], `${port}`)(store);

  return {
    [port]: robotStore,
    [`${port}Axes`]: axes.reduce(
      (o, key) => (
        {
          ...o,
          [`${key}`]: subspace(state => state[`axis${key}`], `axis${key}`)(robotStore)
        }
      ), {}
    )
  }
}

export const unregister = (store, port) => {
  store.detachReducers([port]);
}

Expected Behavior

expect not getting growing number of reducer calls on each usb event

Current Behavior

dynamic reducer mounts perfectly and everything works as expected, but on the 22th usb event when one of my reducer ("controls") gets 31 calls I get error

Unexpected key "USB0" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "controls", "form", "blockField1", "blockField2", "blockField3", "blockField4". Unexpected keys will be ignored.

31 controls reducer default case -------------------------------------------------- controls.js:624

__stack_frame_overlay_proxy_console__ | @ | index.js:2178
warning | @ | warning.js:10
  | combination | @ | combineReducers.js:110
  | p | @ | VM142:1
  | v | @ | VM142:1
  | (anonymous) | @ | VM142:1
  | dispatch | @ | createStore.js:165
  | replaceReducer | @ | createStore.js:195
  | replaceReducer | @ | VM142:1
  | detachReducers | @ | index.js:469
  | unregister | @ | copley.js:67
  | (anonymous) | @ | store.js:72
  | (anonymous) | @ | store.js:71
  | ./node_modules/component-emitter/index.js.Emitter.emit | @ | index.js:133
  | ./node_modules/socket.io-client/lib/socket.js.Socket.onevent | @ | socket.js:278
  | ./node_modules/socket.io-client/lib/socket.js.Socket.onpacket | @ | socket.js:236

after each attach-detach cycle 3 new calls are being added for controls reducer.

Possible Solution

Context

To mount or unmount component I use reducers keys "USB0", "USB1" ...
with one usb device component is still getting mounted and unmounted, but when I have two devices they are not being unmounted, because dynamic reducer is not unmounted by redux.

Your Setup

this is my store

const socket = socketIOClient(SERVER_ADDRESS);

const _createStore = socket => {
  let middlewares = [
    thunk,
    socketIOMiddleware(socket),
    reduxFormMiddleware
  ];
  let middlewareEnhancer;
  if (process.env.NODE_ENV !== 'production') {
    const logger = require('redux-logger').createLogger();
    middlewares = [...middlewares, logger];
    middlewareEnhancer = composeWithDevTools(applyMiddleware(...middlewares));
  } else {
    middlewareEnhancer = applyMiddleware(...middlewares);
  }
  return createStore(
    allReducers,
    middlewareEnhancer,
    dynostore(dynamicReducers())
  );
}
const store = _createStore(socket);
@mpeyper mpeyper added the investigation required We're not sure what's going on yet label Jun 11, 2019
@mpeyper
Copy link
Contributor

mpeyper commented Jun 11, 2019

Hi @AlSharp,

Sorry for the delayed response.

Not sure what's happening here and it's a bit hard to follow without a reproduction repo or code sandbox. Any chance of setting something up to help me debug this?

Unexpected key "USB0" found in previous state received by the reducer. Expected to find one of the known reducer keys instead: "controls", "form", "blockField1", "blockField2", "blockField3", "blockField4". Unexpected keys will be ignored.

This error suggests that the dynamic state might not be merging back together correctly. You also mentioned that you are attaching and detaching pieces of the state so I wonder if you are perhaps hitting this bug? If possible, set the store up for shallow merging (see this comment for an example) and see if the issue goes away.

@AlSharp
Copy link
Author

AlSharp commented Jun 18, 2019

Hi, @mpeyper ,
Thank you for response
I will try to reproduce the case on a small example and share it.

@mpeyper
Copy link
Contributor

mpeyper commented Jan 27, 2021

Closing. Please see #484 for details.

@mpeyper mpeyper closed this as completed Jan 27, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
investigation required We're not sure what's going on yet
Projects
None yet
Development

No branches or pull requests

2 participants