Reputation: 2105
I have pretty simple code, the idea is to fetch some data via ajax and make them available for the whole life of app. I need it this way because I use the Index component in many places, and I cannot afford fetch the data each time I render it. I see via all that messages in console, that the data I need is fetched and the store.state is updated and I see there all data I need, but Index component is never updated, and I don't get this data inside it. I am a novice, so probably I'm just blind to something stupid... I'd also appreciate any piece of advise regarding the whole architecture for my problem.
var React = require('react');
var ReactDOM = require('react-dom');
var Router = require('react-router').Router;
var Route = require('react-router').Route;
var createBrowserHistory = require('history/lib/createBrowserHistory');
var createStore = require('redux').createStore;
var Provider = require('react-redux').Provider;
var connect = require('react-redux').connect;
window.onload=setOptions;
var options = [];
var setOptReducer = function (state = [], action) {
console.log('setOptReducer was called with state', state, 'and action', action)
switch (action.type) {
case 'ADD_ITEM':
return [
...state,
action.data
]
default:
return state;
}
};
var setOptCreator = function (options) {
console.log("options inside setOptCreator ", options);
return {
type: 'ADD_ITEM',
data: options
}
};
var optDispatcher = function(options) {
store.dispatch(setOptCreator(options));
};
const store = createStore(setOptReducer, options);
function setOptions() {
loadFromServer(optDispatcher);
};
function mapStateToProps(state) {
console.log("mapStateToProps ",state);
return {options: state.options};
};
var IndexContainer = React.createClass({
render: function () {
return (
<div>
{console.log("Index rendered with options ", this.props.options)}
</div>
);
}
});
function loadFromServer(callback) {
function option(value, label) {
this.value = value;
this.label = label;
}
$.ajax({
url: "/api/",
dataType: 'json',
cache: false,
success: function (data) {
var options = [];
{.....put some elements from data to options}
console.log("options inside loadFromServer is ", options);
callback(options);
console.log('store state after callback:', store.getState());
}.bind(this)
});
};
var Index = connect(mapStateToProps)(IndexContainer);
ReactDOM.render(
<Provider store={store}>
<Router history={createBrowserHistory()}>
<Route path="/" component={Index}/>
</Router>
</Provider>,
document.getElementById("container")
);
Upvotes: 1
Views: 2764
Reputation: 2105
Finally with huge help of luanped I got to the root of the problem, and I believe it worth putting as a separate answer.
mapStateToProps
really cannot map state.options
to options
as the state
doesn't contain options
attribute, because in the setOptReducer
actionData
is being saved by concatenating it to the state
array, not by putting as a separate named attribute of object state
:
case 'ADD_ITEM':
return [
...state,
action.data
]
So mapStateToProps
doesn't really changes options
(change is undefined
to undefined
) and that's why the component doesn't re-render.
So the decision is to expose the whole state
as options
, which is changing this.props of the component, so it works.
To make it work the more correct way, without exposing the whole state
to the component, but just the options
part, reducer's code should be:
return {
...state,
options: action.data
}
This way state
becomes to have options
attribute, mapStateToProps
sees it and the component re-renders.
Upvotes: 3
Reputation: 3198
Basically in your mapStateToProps, you have options: state.options;
but right now there is no thing named as options in the state. From the docs http://redux.js.org/docs/api/createStore.html, when you use createStore
passing in setOptReducer
as argument, it creates a store with just one reducer and as a result state
on it's own is the value of the store.
Could you try changing your mapStateToProps
to this?
function mapStateToProps(state) {
console.log("mapStateToProps ",state);
return {options: state};
};
If this works, then you could rename some things, since right now it might be confusing. For example removing var options = []
and change the reducer function to options and have
const store = createStore(options, []);
It is recommended to use combineReducers
to create a single root reducer out of many - and if you pass that to createStore
, then the state is an object, and you can then do state.setOptReducer
Hope it's not too confusing
Upvotes: 4