Why My Reducer Is Slow to Storage the Result?
Edit: I Have Adjusted My Code a Bit, and Now I Am Pretty Sure That My Reducer Returns Undefined at the 'Action' No Matter What Comes In. So I Think It Is the...
EDIT: i have adjusted my code a bit, and now i am pretty sure that my reducer returns undefined at the 'action' no matter what comes in. So I think it is the thunk that is the problem, or the combinereducer. Additionally i have return fixed variable and i erhlate nevertheless undefined
I don't see where the error is hiding, but my suspicion is that my reducer is too slow.
What is my goal, my goal is to get all member from one api. For this I work with a combinereducer and thunk.
Additionally I use the await function, the request is properly made and received. Unfortunately the result appears too late and is no longer considered by react, which makes me wonder. So my suspicion is that my reducer is possibly set wrong.
Here is my code:
// file: myAPP/src/index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware} from 'redux';
import thunkMiddleWare from "redux-thunk";
import './style/index.css';
import App from './App';
import reducer from './reducer/index';
let store = createStore(reducer, applyMiddleware(thunkMiddleWare));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
Here is my action which handles the request (myAPP/src/action/action.js):
import axios from 'axios';
export const getMemberList = () => {
return function(dispatch){
axios.get("")
.then(result =>{
dispatch({type: "memberList", payload:result.data})
})
}
}
my reducer which handles to store the result (myApp/src/reducer/member.js) + my combine reducer:
let initialState = [];
const member = (state = initialState, action) => {
if (action?.type === "memberList") {
console.log("member.js: ", action.payload);
return action.payload
} else {
return state
}
}
export default member;
----- NEW FILE -----
import { combineReducers } from 'redux';
import reducer from './reducer';
import members from './member';
let reduce = combineReducers({
memberList: members,
members: reducer
});
export default reduce;
AND at least my APP where the error happing (myApp/src/app.js):
import React from 'react';
import {connect} from 'react-redux';
import './style/App.css';
import Member from './Member';
import { getMemberList } from './action/action';
class App extends React.Component {
constructor(props){
super(props);
this.state = {
data : []
}
this.props.getMemberList();
}
render() {
console.log(this.props) //<--- *3
return (
<div className="App">
{
this.state.data?.length >= 2 ?(
console.log(this.props.membersList) //<---- *4
) : null
}
</div>
);
}
}
let mapStateToProps = function(state){
return ({
membersList: state
})
}
let mapDispatchToProps = {
getMemberList : getMemberList
}
let AppContainer = connect(mapStateToProps,mapDispatchToProps)(App)
export default AppContainer;
All marking from 1-3 there is just empty.
But if I go to 'myApp/src/action/action.js'. I see that it is successful. But as I said in the beginning, I think the saving is faulty.
Anyway it is not rendered and it is not displayed.... so the result see *4 not displayed.
Any idea?
2 Answers
I believe the issue is here - you have to remember that this.props won't update until the next render, so when you do this.setState there, it will always set it to the value before you made the call to getMemberList:
await this.props.getMemberList();
this.setState({
data: this.props.membersList || [] //<---- this will still be empty, this.props
// hasn't updated yet.
});
console.log("data: ", this.props.membersList); //<---- *2
and the order of your logs is incorrect, since this.getTheMemberList(); is not await-ed (and can't be, since it's in a constructor).
So really, you are getting
async getTheMemberList(){
await this.props.getMemberList();
this.setState({
data: this.props.membersList || [] //<---- *2
});
console.log("data: ", this.props.membersList); //<---- *3
}
render() {
console.log(this.props) //<--- *1
The best solution (IMO) is to just not use setState - just use the props directly:
class App extends React.Component {
constructor(props){
super(props);
this.props.getMemberList();
}
render() {
return (
<div className="App">
{
this.props.membersList && this.props.membersList.length >= 2 ? (
console.log(this.props.membersList)
) : null
}
</div>
);
}
}
let mapStateToProps = function(state){
return ({
membersList: state
})
}
let mapDispatchToProps = {
getMemberList : getMemberList
}
let AppContainer = connect(mapStateToProps,mapDispatchToProps)(App)
export default AppContainer;
so for me there are several problems:
- the call to the api
- and the recovery of the state in your App.js some files are missing --> import Member from './Member' , members: reducer so I would focus here on membersList
I also take the liberty of refactoring your code so that the explanation is easier to understand
for redux 3 files : actions.js , reducerMember.js, store.js
//actions.js
import axios from "axios";
const loadMemberList = () => {
return {
type: "loadMemberList",
};
};
const loadMemberListSucess = (items) => {
return {
type: "loadMemberListSucess",
items,
};
};
const loadMemberListError = (error) => {
return {
type: "loadMemberListError",
error,
};
};
export const getMemberList = () => {
return async (dispatch) => {
dispatch(loadMemberList());
try {
const { data } = await axios.get("");
dispatch(loadMemberListSucess(data));
} catch (error) {
dispatch(loadMemberListError(error.message));
}
};
};
//reducerMember.js
const initialState = {
isLoading: false,
error: "",
members: [],
};
const reducerMember = (state = initialState, action) => {
switch (action.type) {
case "loadMemberList":
return { ...state, isLoading: true };
case "loadMemberListSucess":
return { ...state, isLoading: false, members: action.items, error: "" };
case "loadMemberListError":
return { ...state, isLoading: false, members: [], error: action.error };
default:
return state;
}
};
export default reducerMember;
//store.js
import { createStore, combineReducers, applyMiddleware } from "redux";
import thunk from "redux-thunk";
import reducerMember from "./reducerMember.js";
const store = createStore(
combineReducers({
membersList: reducerMember,
//members: reducer --> I don't know what is it the ./reducer
}),
applyMiddleware(thunk)
);
export default store;
Ok now in your index.js
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from "./store"
import './style/index.css';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
And finally your App.js
import React from "react";
import { connect } from "react-redux";
// import Member from './Member' --> I don't know what is it
import { getMemberList } from "./actions";
class App extends React.Component {
componentDidMount() {
this.props.getMemberList();
}
render() {
console.log(this.props.membersList.members);
return (
<div className="App">
{this.props.membersList.members.length > 0 && (
<p>{JSON.stringify(this.props.membersList.members)}</p>
)}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
membersList: state.membersList,
};
};
const mapDispatchToProps = (dispatch) => ({
getMemberList: () => dispatch(getMemberList()),
});
export default connect(mapStateToProps, mapDispatchToProps)(App);