0
votes

The code is long, but the idea is this:

I have 3 Components Base App component, MapComponent , ShipComponent

App components calls out MapComponent using container, MapComponent calls out ShipComponent lso using the container. Both Map and Ship containers are connected to store. Map and Ship component use same multiarray data and is passed to mapStateToProps

In component Ship with the use of useEffect() an action is being called out to generate the data and put it inside the store.

The issue:

The child component (component Ship) re-renders just fine BUT the component Map (parent) does NOT even tho the parent Map component ALSO uses same shipLocation array. Both map and Ship component gets it from same redux store.

Why does child component after executing an action re-renders yet parent component doesn't? How do I fix it?

As a test, Instead of passing the action thru mapDispatchToProps to ShipComponent, I gave it to MapComponent and thru props passed it to ShipComponent for it to execute. Only then it updated the parent MapComponent. But I need ShipComponent to get the action and update MapComponent.

EDIT Added code example:

So root component (App)

import React from "react";
import { Provider } from "react-redux";
import Map from "../containers/Map";
import mainStore from "../store";

const store = mainStore();

function AppComponent(props) {
  return (
    <Provider store={store}>
      <Map />
    </Provider>
  );
}

export default AppComponent;

that calls out container Map that passes all the state data from store to component MapComponent

import { connect } from "react-redux";
import MapComponent from "../components/MapComponent";

const mapStateToProps = ({ shipR }) => {
  return {
    shipLocation: [...shipR.shipLocation],
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
  };
};

const Map = connect(mapStateToProps, mapDispatchToProps)(MapComponent);

export default Map;

MapComponent:

import React from "react";
import { Provider } from "react-redux";
import mainStore from "../store";
import Ship from "../containers/Ship";

const store = mainStore();

function MapComponent(props) {
  return (
    <div id="__Map__">
          {
            <Provider store={store}>
              <Ship />
            </Provider>
          }
    </div>
  );
}

export default MapComponent;

and this MapComponent calls out ShipComponent container

import { connect } from "react-redux";
import ShipComponent from "../components/ShipComponent";
import { generateDefaultShips, shipToggle } from "../actions/shipAction";

const mapStateToProps = ({ shipR }) => {
  return {
    shipLocation: [...shipR.shipLocation],
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    genShips() {
      dispatch(generateDefaultShips());
    },
  };
};

const Ship = connect(mapStateToProps, mapDispatchToProps)(ShipComponent);

export default Ship;

and the ShipComponent component looks like this:

import React, { useEffect } from "react";

function ShipComponent(props) {
  useEffect(() => {
    props.genShips();
  }, []);
  return (
    <div className="ship"></div>
  );
}

export default ShipComponent;

The genShips() is an action:

import { SHIP_GENERATION_RESULT } from "../constants";

export function generateDefaultShips() {
  let arr = [];
  for (let x = 0; x < 7; x++) {
    arr[x] = [];
    for (let y = 0; y < 11; y++) arr[x][y] = null;
  }
  return { type: SHIP_GENERATION_RESULT, ships: arr };
}

The MapComponent reducer:

const initiateState = {
};

function mapReducer(state = initiateState, action) {
  return state;
}

export default mapReducer;

and ShipComponent reducer:

import {
  SHIP_GENERATION_RESULT,
} from "../constants";

const initiateState = {
  shipLocation: [],
};

function shipReducer(state = initiateState, action) {
  switch (action.type) {
    case SHIP_GENERATION_RESULT: {
      return {
        ...state,
        shipLocation: action.ships,
      };
    }
    default:
      return state;
  }
}

export default shipReducer;

and constants index.js contains: export const SHIP_GENERATION_RESULT = "SHIP_GENERATION_RESULT";

All of these containers, constants, components and actions are in different files

p.s. the child component ShipComponent gets the shipLocation multiarray data just fine, but since MapComponent (parent) does not re-render he does not.

O and the reduxers index.js:

import { combineReducers } from "redux";
import mapReducer from "./mapReducer";
import shipReducer from "./shipReducer";

const allReducers = combineReducers({
  mapR: mapReducer,
  shipR: shipReducer,
});

export default allReducers;

store index.js:

import { createStore, applyMiddleware } from "redux";
import thunkMiddleware from "redux-thunk";
import allReducers from "../reducers";
import { composeWithDevTools } from "redux-devtools-extension";

export default function mainStore(prevState) {
  return createStore(
    allReducers,
    prevState,
    composeWithDevTools(applyMiddleware(thunkMiddleware))
  );
}
1
Could you provide the reducer for that genShips() or a minimal reproducible example?dongnhan
Yeah sure. imma remove all the not needed code to leave a bare minimum example. Ill update it shortlyLith
@dongnhan added all components, reducers and action code while removing the not needed code for this example. tested - same issue still accures with this minimum codeLith
you are using different store for App and Map component, remove the store and its provider in MapComponent should be helped.tuan.tran
@tuan.tran OMG thank you! I tough i needed to repeat <provider> for each component that uses the store. If you would like to post the answer as a 'answer' instead of a comment so I could mark it as "answer" that would be great so others who have made same mistake would have a quick answer. Thx again! :)Lith

1 Answers

1
votes

You are using different store for App and Map component, remove the store and its provider in MapComponent should be helped. There should only 1 redux store in all app to make all data is consistent.