15

Using React, Redux, Redux-thunk I want to have an initial state through a server request (API call) but I cannot seem to get this to work.

What I have got so far:

var Redux = require('redux');
var carReducer = require('./reducers/cars');
var thunk = require('redux-thunk').default;

var initialState = require('./reducers/initialState');

var rootReducer = Redux.combineReducers({
    cars: carReducer
});

module.exports = Redux.applyMiddleware(thunk)(Redux.createStore)(rootReducer, initialState.loadInitial());

This is my initial store creation. My InitialState looks like this:

var $ = require('jquery');

module.exports = {
    loadInitial: loadInitial
};

function loadInitial() {
    return {
        cars: [
            {}
        ]
    }
}

When I try to turn this loadInitial into a $.get('api/...'), Redux tells me that I need an initial state in order for it to work.

In my reducer I have a load method:

function updateReducer(state, action) {
    switch (action.type) {
        case 'load':
            return action.data;
        case 'update':
            return updateCars(action.data, state);
        default:
            return action.data || initialState.loadInitial().cars;
    }
};

But again, if I use - as a default - an async call, it does not seem to work.

What I actually want is for my store to be initialized with whatever comes from the database. This is needed for my HandsonTable, since I pass the properties to the table and as I have it now, it will only render me one row because my initial state has only one object.

Weird part about this is that when I click on the table, it actually gets me all my rows because the data is loaded, but I'm guessing just too late.

Anyway, my question here is, how do I initialize my store through an api call to my backend?

Carl Manaster
  • 38,312
  • 15
  • 96
  • 147
Tikkes
  • 4,219
  • 4
  • 30
  • 57

2 Answers2

5

You can give it a blank initial state (empty object or perhaps just a loading message) and when your API call comes back, you can update the state and your component will redraw with its new data as a consequence. The other thing you can do is not initialize your component until the call returns with your data.

EDITED TO INCLUDE EXAMPLE

In one of your lifecycle methods of your component (perhaps even in getInitialState):

getInitialState: function() {
  var oThis = this;

  $.get({
    url: 'api...',
    success: function (...) {
      oThis.setState({...data returned by server});
    }
  });

  return {};
}

Or, something along these lines:

$.get({
  url: 'api...',
  success: function (...) {
    var oStore;

    // oStore = response from server

    ReactDOM.render(
      <ReactRedux.Provider store={oStore}>
        <MyComponent/> 
      </ReactRedux.Provider>
      , document.getElementById()
    );
  }
});
Carl Manaster
  • 38,312
  • 15
  • 96
  • 147
Christoph
  • 833
  • 6
  • 14
  • 1
    Any example of this perhaps? I've been trying this all morning already – Tikkes May 25 '16 at 11:44
  • 1
    The second one actually works! first one didn't because here, I do not have access to my store yet (it hasn't been created). Only thing I'm wondering now, doesn't this create alot of overhead? (reload the store every time?) – Tikkes May 25 '16 at 13:53
  • It is going to reload the store every time. But how often are you getting new data from the server? In any case, even if the store/state does update, react will only ever do anything with the nodes/components that actually changed. – Christoph May 25 '16 at 16:45
  • 1
    Thanks for the help Christoph! I accept your answer – Tikkes May 26 '16 at 06:08
4

For redux initial state you can't pass async value, so you need to pass some static empty object (like you have in your example) and call an action to get initial state from server and replace your state. To simplify all this process you can use redux async initial state middleware

Alexandr Subbotin
  • 1,614
  • 2
  • 16
  • 16