The following code works, but does not re-drive the app render() upon a successful redux dispatch call. I cannot see what is wrong. There is mocked up data here I put in to simplify things trying to debug this rudimentary problem.
What works:
- The app react component renders the first time
- The dispatcher successfully invokes the reducer
- The reducer successfully invokes the mapStateToProps() with the correct state change data
What fails:
- The app re-render is never invoked upon the successful dispatch/reducer state change
index.js
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import ReactDOM from 'react-dom';
import './css/index.css';
import App from './containers/App';
import reducer from './reducers';
const store = createStore(
reducer,
{schooldata: {}}
)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
store.dispatch({type: "LOAD_SCHOOL_DATA"});
containers/App.js
import React from 'react';
import { connect, Provider } from 'react-redux';
import {
Route,
NavLink,
BrowserRouter as Router,
Switch,
} from 'react-router-dom';
import Home from "../components/Home";
import Photos from "../components/Photos";
import Attendees from "../components/Attendees";
class App extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
};
render() {
console.log("App is rendering...");
console.log("Props in app: %s", JSON.stringify(this.props));
const src=`${this.props.images}/pic.jpg`;
console.log("src: %s", src);
return (
<div>
<Router>
<div>
<ul>
<li>
<NavLink exact activeClassName="active" to="/">
Home
</NavLink>
</li>
<li>
<NavLink activeClassName="active" to="/photos">
Photos
</NavLink>
</li>
<li>
<NavLink activeClassName="active" to="/attendees">
Attendees
</NavLink>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/photos" component={Photos} />
<Route path="/attendees" component={Attendees} />
</Switch>
</div>
</Router>
</div>
)
}
}
const mapStateToProps = (state) => {
console.log("Mapping this: %s", JSON.stringify(state));
return (state.schooldata);
}
const mapDispatchToProps = function (dispatch) {
console.log("Mapping dispatch to props");
return({});
}
export default connect(mapStateToProps)(App);
reducers/index.js
import axios from 'axios';
import { combineReducers } from 'redux';
import { LOAD_SCHOOL_DATA, ADD_PHOTO, ADD_ATTENDEE } from '../constants/ActionTypes';
const routeAction = (state, action) => {
console.log("routeAction: %s", JSON.stringify(action));
switch(action.type) {
case LOAD_SCHOOL_DATA:
console.log("Action type match.");
return ({
schooldata: {images:"blah", photos:"bleep"},
});
case ADD_PHOTO:
return addPhoto(state);
case ADD_ATTENDEE:
return addAttendee(state);
default:
return {};
}
}
const loadSchoolData = async () => {
await axios.get("/schooldata").then((response) => {
console.log("School data from server: %s", JSON.stringify(response.data));
return(response.data);
})
}
const addPhoto = (state) => {
return state.loadSchoolData.photoList
}
const addAttendee = (state) => {
}
export default combineReducers({
routeAction
});
Browser debug output
routeAction: {"type":"@@redux/INITx.t.b.e.s"} index.js:6:12
routeAction: {"type":"@@redux/PROBE_UNKNOWN_ACTION0.c.1.k.j.c"} index.js:6:12
routeAction: {"type":"@@redux/INITx.t.b.e.s"} index.js:6:12
Mapping this: {"routeAction":{}} App.js:64:10
App is rendering... App.js:24:12
Props in app: {} App.js:25:12
src: undefined/pic.jpg App.js:27:12
props: {"history":{"length":2,"action":"POP","location":{"pathname":"/","search":"","hash":""}},"location":{"pathname":"/","search":"","hash":""},"match":{"path":"/","url":"/","isExact":true,"params":{}}} Home.js:15:12
routeAction: {"type":"LOAD_SCHOOL_DATA"} index.js:6:12
Action type match. index.js:9:20
Mapping this: {"routeAction":{"schooldata":{"images":"blah","photos":"bleep"}}} App.js:64:10
routeAction: {"type":"LOAD_SCHOOL_DATA"} index.js:6:12
Action type match. index.js:9:20
Mapping this: {"routeAction":{"schooldata":{"images":"blah","photos":"bleep"}}} App.js:64:10
routeAction: {"type":"LOAD_SCHOOL_DATA"} index.js:6:12
Action type match. index.js:9:20
Mapping this: {"routeAction":{"schooldata":{"images":"blah","photos":"bleep"}}} App.js:64:10
return (state.schooldata)- based on your console output, this isundefined, I think you wantreturn (state.routeAction.schooldata)- Robin ZigmondrouteActionproperty holds your whole state because you are usingcombineReducerswith just one reducer, calledrouteAction) - Robin ZigmondmapStateToPropsdoesn't pick up the change (because it always returnsundefined). Connecting your component to Redux doesn't change the fundamentals that the component will only rerender if it receives new props (or state). The sequence is: store updates -> mapStateToProps is executed (as you saw) -> it's return value is merged into the component's actual props -> React checks for changes from the last render, and if so, rerenders. In your case there are no changes because of the incorrectmapStateToProps- Robin Zigmond