trying to share the states from one component to another: The state can be accessed from main component but it comes undefined when accessing from a new component
This is my reducer:
export const tableReducer=(state = [], action)=> {
switch (action.type) {
case 'SELECTED_LIST':
state = JSON.parse(JSON.stringify(action.payload));
return state;
default:
return state
}
}
access it from a different file:
const [userList, usersDispatch] = useReducer(tableReducer, []);
useEffect(() => {
const list = Object.keys(selectedRowIds).length > 0 ? selectedFlatRows.map(
d => d.original.email
)
: '';
usersDispatch({ type: 'SELECTED_LIST', payload: list, });
}, [selectedRowIds, selectedFlatRows]);
and in a new component:
const [userList] = useReducer(tableReducer);
const deleteUsers = () => {
console.log(userList)
}
but here console.log(userList) it results to undefined
For Sharing of state between components, you can use Context API with useReducer.
Context API provides a neat way of providing state to child components without ending up with a prop drilling situation. It requires that a Provider is setup, which provides its values to any of its Consumers. Any component that is a child of the Provider can consume the context.
First a piece of context is created.
CustomContext.js
import React from 'react';
const CustomContext = React.createContext();
export function useCustomContext() {
return React.useContext(CustomContext);
}
export default CustomContext;
We can define your reducer in a seperate file.
TableReducer.js
export const tableReducer=(state = [], action)=> {
switch (action.type) {
case 'SELECTED_LIST':
state = JSON.parse(JSON.stringify(action.payload));
return state;
default:
return state
}
}
next is to implement the provider, and give it a value within a "Parent" component (A higher up component)
Parent.js
import CustomContext from './CustomContext'
import { tableReducer } from './TableReducer'
const ParentComponent = () => {
const [userState, usersDispatch ] = React.useReducer(tableReducer, []);
const providerState = {
userState,
usersDispatch
}
return (
<CustomContext.Provider value={providerState} >
<ChildComponent /> //Any component within here can access value by using useCustomContext();
</CustomContext.Provider>
)
}
now any component nested within <CustomContext.Provider></CustomContext.Provider> can access whatever is passed into "value" prop of the Provider which is your context state and the dispatch method.
The child component will look like this (I have ommited your state values and such..)
Child.js
import { useCustomContext }from './CustomContext'
const ChildComponent = (props) => {
//your custom state variables and other methods
const { userState, usersDispatch } = useCustomContext();
useEffect(() => {
const list = Object.keys(selectedRowIds).length > 0 ? selectedFlatRows.map(
d => d.original.email
)
: '';
usersDispatch({ type: 'SELECTED_LIST', payload: list, });
}, [selectedRowIds, selectedFlatRows]);
return(
<div>your components dependent on selectedRowIds, selectedFlatRows<div>
)
}
You can't share the state with useReducer hook like you are trying to. Each call to useReducer returns a new state that is managed using the reducer function passed to useReducer hook.
Just as each call to useState returns a different state, each call to useReducer returns a different state. Two useReducer calls can't share the same state.
To share the state, you can use one of the following options:
Context API
React-Redux
Pass the state from parent component to child component using props
#Gandzal is correct but I found it was lacking a typscript version and also today createContext requieres a default parameter. This came up as one of the top answers on google so I thought I would share.
I setup my solution like this:
Custom context:
import React, {Dispatch} from 'react';
import {StateType, Action} from './reducer'
interface IContextProps {
state: StateType;
dispatch:Dispatch<Action>
}
const CustomContext = React.createContext({} as IContextProps);
export function useCustomContext() {
return React.useContext(CustomContext);
}
export default CustomContext;
Note StateType and Action:
export type StateType = {
items: Array<DataItems>;
date: Date;
};
export type Action = {
type: ActionKind;
payload: DataItems;
};
reducer:
export const reducer = (state: StateType, action: Action) => {
const { type, payload } = action;
let newArray: Array<DataItems> = [];
switch (type) {
case ActionKind.Checked:
newArray = state.items.map((item) => ({
...item,
checked: item.id === payload.id ? true : item.checked,
}));
return {
...state,
items: newArray,
}
default:
return state;
}
};
App.tsx:
import { reducer, initalState } from 'Shared/Reducer/reducer';
import CustomContext from 'Shared/Reducer/CustomContext';
const App: React.FC = () => {
const [state, dispatch] = React.useReducer(reducer, initalState);
const providerState = {
state,
dispatch,
};
return (
<CustomContext.Provider value={providerState}>
<main role="main">
// your components
</main>
</CustomContext.Provider>
);
};
export default App;
And one of your components:
import { useCustomContext } from 'Shared/Reducer/CustomContext';
export const MyComp: React.FC<MyType> = (props) => {
const { data} = props;
const { state, dispatch } = useCustomContext(); --- Your state and dispatch here
return (
<div>
// your component
</div >
);
}
Related
I actually have 2 components that require data from a reducer that has some state there. The CurrentWeather component shows the weather at a very high level and the CurrentWeatherDetailed in a detailed way. The first one is rendered first, and if clicked on Details it will render CurrentWeatherDetailed. CurrentWeather dispatches an action to fetch some data and update the state with that data, later it gets the state through useReducer and it renders the data from that state as expected. On the other hand, CurrentWeatherDetailed, also tries to get the state that is currently populated, but it just returns null(which is the initialState) instead of what is actually in the state, considering that CurrentWeather, I assume; has already changed the state. So I have tried a lot but I have not found an explanation of why this happens.
This is the reducer:
export const initialState = {
loading: false,
error: null,
data: null,
}
const weatherRequestReducer = (state = initialState, action) => {
switch (action.type) {
case 'SEND':
return {...state,
loading: true,
error: null,
data: null,
};
case 'RESPONSE':
console.log(state);
console.log(action.responseData);
return {...state,
loading: false,
data: action.responseData
};
case 'ERROR':
return {...state,
loading: false,
error: action.errorMessage,
data: null
};
case 'CLEAR-ERROR':
return {...state,
error:null
};
default:
return state;
// throw new Error('Should not get there')
}
}
export default weatherRequestReducer;
And here is a hook that uses the reducer to generate a state and return it to other components:
import {useReducer, useCallback, useState, useEffect} from 'react';
import weatherRequestReducer from "../reducers/weatherRequestReducer";
import {initialState} from "../reducers/weatherRequestReducer";
import api from '../utils/WeatherApiConfig'
const useCurrentWeather = () => {
const [weatherRequestState, dispatchWeatherRequestActions] = useReducer(weatherRequestReducer, initialState);
const sendRequestToGetCurrentWeather = useCallback(() => {
dispatchWeatherRequestActions({type: 'SEND'});
const uri = 'weather?q=London,uk';
api.get(uri)
.then(res => {
const responseData = res.data;
console.log(responseData.base);
dispatchWeatherRequestActions({type: 'RESPONSE', responseData: responseData});
})
.catch(err => {
console.log(err);
dispatchWeatherRequestActions({type: 'ERROR', errorMessage: 'Something went wrong!'});
});
}, []);
return {
getCurrentWeatherPointer: sendRequestToGetCurrentWeather,
isLoading: weatherRequestState.loading,
error: weatherRequestState.error,
data: weatherRequestState.data,
}
}
export default useCurrentWeather;
Here is the App component calling the CurrentWeather and CurrentWeatherDetailed, depending on a dummy state:
import CurrentWeather from "./components/CurrentWeather/CurrentWeather";
import CurrentWeatherDetailed from "./components/CurrentWeather/CurrentWeatherDetailed";
import {useState} from "react";
import weatherRequestReducer from "./reducers/weatherRequestReducer";
function App() {
const [state, setState] = useState(0);
const renderComponent = () => {
setState(1)
}
let comp = <CurrentWeather renderComponent={renderComponent}/>;
if(state === 1){
comp = <CurrentWeatherDetailed/>
}
return {comp}
}
export default App;
The following component is working fine and console.log(data), shows the actual data that has been asked for.
import React, {useEffect} from 'react';
import useCurrentWeather from "../../hooks/useCurrentWeather";
const CurrentWeather = (props) => {
const {getCurrentWeatherPointer, isLoading, error, data} = useCurrentWeather();
useEffect(() => {
getCurrentWeatherPointer();
}, [getCurrentWeatherPointer])
useEffect(() => {
console.log(data);
}, [data]);
const displayDetails = () => {
props.renderComponent();
}
return ( <Some JSX>
<div className={classes.buttonContainer}>
<button onClick={displayDetails}>Details →</button>
</div>
<Some JSX> );
}
export default CurrentWeather;
Here is the component that is failing, because is getting data as null instead of what has been already fetched by the CurrentWeather component:
import React, {useEffect} from 'react';
import useCurrentWeather from "../../hooks/useCurrentWeather";
const CurrentWeatherDetailed = (props) => {
const {getCurrentWeatherPointer, isLoading, error, data} = useCurrentWeather();
useEffect(() => {
console.log(data)
setTimeout(() => {console.log(data);}, 500 );
}, [data])
return ( <Some JSX> );
}
export default CurrentWeatherDetailed;
If I just add useEffect(() => {getCurrentWeatherPointer();},[getCurrentWeatherPointer]) to CurrentWeatherDetailed it works fine as CurrentWeather, but, I do not want to do that because I already loaded that from the API to the reducer state, so it does not make sense to load that again, but to use what is already there when calling this detailed component.
Does anyone know why is this happening?
Thanks in advance guys.
The reason why this is happening is you are rendering only one component at a time and your components are siblings to each other. when your weather component fetches the data there is no weather details component . So when you click on the details button you are mounting the CurrentWeatherDetailed meaning you are creating the component from scratch. At this point your CurrentWeatherDetailed is not aware of what CurrentWeather had done because components are isolated from each other and the only way of sharing information between them is via props . So your CurrentWeatherDetailed initializes the reducer again.
You should move your useReducer to the App and remove it from CurrentWeatherDetailed and CurrentWeather . So that you can share the information between the CurrentWeatherDetailed and CurrentWeather.
function App() {
const [state, setState] = useState(0);
const {getCurrentWeatherPointer, isLoading, error, data} = useCurrentWeather();
const renderComponent = () => {
setState(1);
};
let comp = <CurrentWeather renderComponent={renderComponent} getCurrentWeatherPointer={getCurrentWeatherPointer}/>;
if (state === 1) {
comp = <CurrentWeatherDetailed data={data} />;
}
return {comp};
}
export default App;
You have a misconception there:
useReducer does not share state over components. It is, like useState part of React and only to be used for local component state.
Data is not shared "in the reducer" (which is just a function), it only uses the reducer to describe the transitions of the state based on actions you dispatch.
If you want this data shared between components you have to move it up to a common parent and pass it down to all children that require it using props or context, or just use a global state management solution like Redux.
New to React. I am trying out react redux for the first time (on my own). I have a state for a gameboard called force_hidden that I want to set in App.js and then use in a child component ( a few levels down). I used redux to create forceGameBoardHidden that should set force_hidden to whatever value is inside the (). so, forceGameBoardHidden(true) should set the state of force_hidden to true. However, that doesn't happen. I can click on the item and it logs "before change" and then the state. In between it should have set the state to true, but the state is still false. I don't know what's going wrong here. I tried console.logging the gameBoardReducer. It fires when I start the page, but doesn't fire when I click the button.
gameboard.types.js
const GameBoardActionTypes = {
FORCE_GAMEBOARD_HIDDEN: 'FORCE_GAMEBOARD_HIDDEN'
}
export default GameBoardActionTypes;
gameboard.action.js
import GameBoardActionTypes from './game-board.types';
export const forceGameBoardHidden = value => ({
type: GameBoardActionTypes.FORCE_GAMEBOARD_HIDDEN,
payload: value
});
gameboard.reducer.js
import GameBoardActionTypes from './game-board.types'
const INITIAL_STATE = {
force_hidden: false
}
const gameBoardReducer = ( state = INITIAL_STATE, action) => {
switch (action.type) {
case GameBoardActionTypes.FORCE_GAMEBOARD_HIDDEN:
return {
...state,
force_hidden: action.payload
}
default:
return state;
}
}
export default gameBoardReducer;
root-reducer
import { combineReducers } from 'redux';
import gameBoardReducer from './game-board/game-board.reducer'
export default combineReducers ({
gameboard: gameBoardReducer
})
store.js
const middlewares = [];
const store = createStore(rootReducer, applyMiddleware(...middlewares))
export default store;
index.js
<Provider store={store}>
App.js -- this is where the magic should happen in forceGameBoardHidden
const App = () => {
const handleKeyChange = event => {
setKey(event.target.value);
console.log("before change")
forceGameBoardHidden(true)
console.log(store.getState().gameboard)
}
return (
<SearchBox
onChange={handleKeyChange}
placeholder="Enter your game Key"/>
</div>
);
}
const mapDispatchToProps = dispatch => ({
forceGameBoardHidden: item => dispatch(forceGameBoardHidden(item))
})
export default connect(null,mapDispatchToProps)(App);
I think you need to dispatch the action, there are 2 methods , one is to connect the component to the actions and bind them to dispatch. The other one is much easier since you use functional components, is by using the useDispatch hook
Example here:
import { useDispatch } from 'react-redux' // <-- add this
const App = () => {
const dispatch = useDispatch() // <-- add this
const handleKeyChange = event => {
setKey(event.target.value);
console.log("before change")
dispatch(forceGameBoardHidden(true)) // <-- change this
console.log(store.getState().gameboard)
}
return (
<SearchBox
onChange={handleKeyChange}
placeholder="Enter your game Key"/>
</div>
);
}
Problem statement: The class render() is called but the underlying input box does not update field.
Description: When the app Class is loaded, I make a fetch config call to get the initial server config. Before the async calls, the input box successfully shows the initial value. However, after the async data is loaded, the class calls the render method but input value would not change.
Code:
InputBoxComponent
import React, {useState} from 'react';
export const StatefulInputBox = (props) => {
const [inputValue, changeInputValue] = useState(props.value);
console.log(inputValue);
const onChange = (e) => {
const val = e.target.value;
changeInputValue(val);
if(props.onChange) {
props.onChange(val);
}
}
return (
<input value={inputValue} onChange={(e) => {
onChange(e)
}} />
)
};
WrapperClass
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {nameActions} from "../../actions/inputBoxActions";
import {StatefulInputBox} from "./StatefulInputBox";
class StatefulNumInputBoxWrapperStoreComponent extends Component{
constructor(props) {
super();
console.log(props.inputConfig);
props.getThunkNum();
this.onValueChange = this.onValueChange.bind(this);
}
//props.getInputBoxNum();
// Without a button this goes in an infinite self rendering loop
// That is why we use a class wrapper, so that the constructor isn't called multiple times
onValueChange (newVal) {
console.log(newVal);
}
render() {
return (
<div>
<StatefulInputBox value={this.props.inputConfig.value} onChange={this.onValueChange}/>
</div>
);
}
}
const mapInputWrapperStateToProps = (state) => {
return {
inputConfig: state.inputBox
}
}
const mapInputWrapperDispatchToProps = (dispatch) => {
return {
getInputBoxNum: () => (dispatch(nameActions.getInputBoxNum())),
setInputBoxNum: (name) => (dispatch(nameActions.setInputBoxNum(name))),
getThunkNum: () => (dispatch(nameActions.getThunkNum()))
}
}
export const StatefulNumInputWrapperStoreClass = connect(mapInputWrapperStateToProps, mapInputWrapperDispatchToProps)(StatefulNumInputBoxWrapperStoreComponent);
InputBoxAction
export const nameActions = {
getInputBoxNum: () => {
return {
type: 'GET_INPUTBOX_NUM',
payload: {
loading: false,
value: 10
}
}
},
setInputBoxNum: (num) => {
return {
type: 'SET_INPUTBOX_NUM',
payload: {
loading: false,
value: num
}
}
},
getThunkNum: () => {
return (dispatch) => {
dispatch(nameActions.setLoading());
setTimeout(()=>{
dispatch(nameActions.setInputBoxNum(50));
},2000)
}
}
};
InputBoxReducer
const initialState = {
value: 'Joe',
loading: false
}
export const nameReducer = (currentState = {...initialState}, action) => {
switch (action.type) {
case 'GET_INPUTBOX_NUM': {
const newState = {...currentState, ...action.payload};
return newState;
}
case 'SET_INPUTBOX_NUM': {
const newState = {...currentState, ...action.payload};
return newState;
}
case 'NAME_SUCCESS': {
let newState = {...currentState, ...action.payload};
return newState;
}
default: {
return currentState;
}
}
}
Store
import {createStore, combineReducers, applyMiddleware} from 'redux';
import {buttonReducer} from "./components/Button/buttonReducers";
import {nameReducer} from "./reducers/inputBoxReducer";
import thunk from 'redux-thunk';
import {dropDownReducer} from "./reducers/dropDownReducer";
const rootReducer = combineReducers({
button: buttonReducer,
inputBox: nameReducer,
dropDown: dropDownReducer
});
export const store = createStore(rootReducer, applyMiddleware(thunk));
Few things to note:
All the data flow points are indicating change, meaning the constructor, the actions, the reducer, the mapStateToProps, then the class render. What is not working is the inputBox value. The console log inside the InputBox is not called after the async call.
I have used controlled form as there would be prefilled data.
I tried the wrapper with a pure component but it goes on an infinite loop as getThunkNum keeps getting called repetitively
Also suggest, what would be a better strategy to work with a default prefilled data, initial config load data, and then the normal flow of editing.
UPDATE 1
I tried the fetch API inside componentDidMount() of the wrapper class but still no luck.
UPDATE 2
I am receiving value from props but my input value is pointing to inputValue, a property of state object. So how do I decide if I should print the props or state property?
Okay, I figured out the problem.
When you have the issue of showing a default value, a pre-loaded server data (pre-fill) AND making them editable. It's best not to mix props and useState. A direct redux approach of changing the store value on every change solves the problem.
In the above problem, the inputBox looked like this:
import React, {useState} from 'react';
export const StatefulInputBox = (props) => {
const [inputValue, changeInputValue] = useState(props.value);
console.log(inputValue);
const onChange = (e) => {
const val = e.target.value;
changeInputValue(val);
if(props.onChange) {
props.onChange(val);
}
}
return (
<input value={inputValue} onChange={(e) => {
onChange(e)
}} />
)
};
Notice, the ambiguity while assigning value to the input box. If you assign props.value, then the user can't type. And if you assign value=inputValue then it can't be changed by the server call data.
I removed the useState feature and directed changes through redux itself
export const DelayedResInputBox = (props) => {
return (
<input value={props.value} onChange={(e) => {
props.onChange(e.target.value)
}} />
)
};
And in the wrapper class, I wired the updates to store using redux
class DelayedResWrapperComponent extends Component {
constructor() {
super();
this.onChange = this.onChange.bind(this);
}
onChange(val) {
console.log(val);
this.props.saveUserResponse(val)
}
componentDidMount() {
this.props.getServerData();
}
render() {
return (
<DelayedResInputBox value={this.props.delayedRes.value} onChange={this.onChange}/>
)
}
}
const mapDelayedDispatchToProps = (dispatch) => {
return {
getServerData: () => {
dispatch(delayedResActions.getDelayedThunkRes())
},
saveUserResponse: (val) => {
dispatch(delayedResActions.setValueInStore(val))
}
}
};
This way all the minor changes are routed through store and the inputBox gets only one change command. Hence, I achieved the goal by updating store via redux.
Note: Change in store does not mean submit. All smaller components will update store with their value and then a submit button will only trigger a server update. This also helps to pull out unnecessary logic in a dumb component
The only other approach would be to have three input boxes: a disabled one to show data till the config loading completes, an editable one with useState
Feel free to correct me if I am wrong.
i have a problem when dispatching an action inside useEffect in functional component
in class component it is looks like this
import getAllItem from './actions/item'
export class Home extends Component {
state = { menus: [] }
componentDidMount() {
this.props.dispatch(getAllItem())
this.setState({ menus: this.props.menu })
})
}
...
...
const mapStateToProps = state => {
return { menu: state.reItem.itemList}
}
export default connect(mapStateToProps)(Home);
in console it's return an array of my menus
i want convert it into functional component ,and i've tried
import getAllItem from './actions/item'
export default function Home() {
const dispatch = useDispatch();
const menuState = useSelector(state => state.reItem.itemList)
const [menus, setMenus] = useState();
useEffect(() => {
dispatch(getAllItem())
setMenus(menuState);
}, [])
...
...
i expected my functional component have the same result as class component do ,but in console it returning
"TypeError:(0,_item.default)is not a function"
my action code is simple
export const getAllItem = () => {
return {
type: 'GET_ITEM',
payload: Axios.get(`${URL}/item/`)
}
}
and the reducer
const initialState = {
itemList: []
}
const item = (state = initialState, action) => {
switch (action.type) {
case 'GET_ITEM':
return {
...state,
itemList: action.payload.data.result
}
You are importing named exports incorrectly.
your code:import getAllItem from './actions/item'
correct import:import {getAllItem} from './actions/item'
Using the terminal to test my dispatched actions, Redux-logger shows that my state is being correctly updated. However, my component is not re-rendering as a result of the state change. I've looked at the SO answers regarding component not re-rendering, a majority of the responses claim that the state is being mutated; as a result, Redux won't re-render. However, I'm using Lodash's merge to deep-dup an object, I'm pretty sure I'm not returning a modified object. (Please see attached snippet below)
Would love to hear some advice from you guys, pulling my hair out on this one!
const usersReducer = (state = {}, action) => {
Object.freeze(state); // avoid mutating state
console.log(state);
// returns an empty object
let newState = merge({}, state);
console.log(newState);
// returns my state with my dispatched action object inside already???
// newState for some reason already has new dispatched action
switch (action.type) {
case RECEIVE_USER:
let newUser = {[action.user.id] = action.user};
return merge(newUser, newUser);
case RECEIVE_USERS:
newState = {};
action.users.forEach(user => {
newState[user.id] = user;
});
return merge({}, newState);
default:
return state;
}
};
React Container Component
import { connect } from 'react-redux';
import { receiveUsers, receiveUser, refreshAll, requestUsers, requestUser } from '../../actions/user_actions';
import allUsers from '../../reducers/selectors';
import UserList from './user_list';
const mapStateToProps = (state) => ({
users: allUsers(state), // allUsers (selector that takes the state specfically the user Object and returns an array of user Objects)
state
});
const mapDispatchToProps = (dispatch) => ({
requestUser: () => dispatch(requestUser()),
requestUsers: () => dispatch(requestUsers()),
receiveUsers: (users) => dispatch(receiveUsers(users)),
receiveUser: (user) => dispatch(receiveUser(user)),
refreshAll: (users) => dispatch(refreshAll(users))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(UserList);
React Presentational component
import React from 'react';
class UserList extends React.Component {
render() {
const { users, state } = this.props;
const userItems = users.map((user, idx) => {
return(<li key={idx}>{user.username}</li>);
});
return (
<div>
<ul>
{ userItems }
</ul>
</div>
);
}
}
export default UserList;
React Store
import { createStore, applyMiddleware } from 'redux';
import createLogger from 'redux-logger';
import RootReducer from '../reducers/root_reducer';
const logger = createLogger();
const configureStore = (preloadedState = {}) => {
return createStore(
RootReducer,
preloadedState,
applyMiddleware(logger));
};
// const configureStore = createStore(rootReducer, applyMiddleware(logger));
// oddly enough, when I have the store as a constant and not a function that returns the store constant, dispatching actions through the terminal will correctly update the state and rerender the component
export default configureStore;
React Selector
const allUsers = ({ users }) => {
return Object.keys(users).map(id => (
users[id]
));
};
export default allUsers;
I had a similar problem, just in case someone stumbles upon this, I needed to clone the array in order to re-render the view:
export const addFieldRow = () => (
(dispatch: any, getState: any) => {
const state = getState();
const myArrayOfObjects = myArrayOfObjectsProp(state);
const newObject = {
key: "",
value: "",
};
myArrayOfObjects.push(newObject);
dispatch(addFieldRowAction({ myArrayOfObjects: [...myArrayOfObjects] })); <== here
}
);
Common problem in this case is using not reactive operations for changing state. For example use concat() for array, but not push() and so on.
I use this solution to do it.
I put the users on my state and update it on any change, with componentWillReceiveProps. Hope it helps :-)
class UserList extends React.Component {
constructor(props) {
super(props);
console.log(this.props);
this.state = {
users: props.users
};
}
componentWillReceiveProps(nextProps) {
if (this.props.users !== nextProps.users) {
this.setState({
users: nextProps.users,
});
}
}
render() {
const { users } = this.state;
const userItems = users.map((user, idx) => {
return(<li key={idx}>{user.username}</li>);
});
return (
<div>
<ul>
{ userItems }
</ul>
</div>
);
}
}
export default UserList;
What do your React components look like? Are you using internal state in them or props to push the data down. Usually I see the issue is with people setting the internal state of props with Redux state. You should be pushing props down to the components and they will re-render on update.
Also, check out
https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=en
to see if the props are really changing.
Create new copy of array from the prop state to re-render the component
render() {
const {allPost} = this.props;
//Use the spread operator to create a new copy of the array
const posts = [...allPost];
const plansList = () => {
return posts.length < 1 ? null : posts && <PlansList allPost={posts}
/>;
};
return (
<>
<Container className="mt-lg-5 pt-lg-5">
{plansList()}
</Container>
</>
);
}
i spent lot of time to find out that when using more than 1 reducer (with combineReducers), then your mapStateToProps should point the correct reducer names, for example
const mapStateToProps = state => ({
someVar: state.yourReducerName.someVar,
loading: state.yourReducerName.loading,
error: state.yourReducerName.error
});