Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create API calls in REACT and FLUX

I'm new to react and flux and I am having a hard time trying to figure out how to load data from a server. I am able to load the same data from a local file with no issues.

So first up I have this controller view (controller-view.js) that passes down initial state to a view (view.js)

controller-view.js

var viewBill = React.createClass({
getInitialState: function(){
    return {
        bill: BillStore.getAllBill()
    };
},
render: function(){
    return (
        <div>
            <SubscriptionDetails subscription={this.state.bill.statement} />
        </div>
    );
}
 });
 module.exports = viewBill;

view.js

var subscriptionsList = React.createClass({
propTypes: {
    subscription: React.PropTypes.array.isRequired
},
render: function(){

   return (
        <div >
            <h1>Statement</h1>
            From: {this.props.subscription.period.from} - To {this.props.subscription.period.to} <br />
            Due: {this.props.subscription.due}<br />
            Issued:{this.props.subscription.generated}
        </div>
    );
}
 });
 module.exports = subscriptionsList;

I have an actions file that loads the INITAL data for my app. So this is data that is not called by as user action, but called from getInitialState in the controller view

InitialActions.js

var InitialiseActions = {
initApp: function(){
    Dispatcher.dispatch({
        actionType: ActionTypes.INITIALISE,
        initialData: {
            bill: BillApi.getBillLocal() // I switch to getBillServer for date from server
        }
    });
}
};
module.exports = InitialiseActions;

And then my data API looks like this

api.js

var BillApi = {
getBillLocal: function() {
    return billed;
},
getBillServer: function() {
    return $.getJSON('https://theurl.com/stuff.json').then(function(data) {

        return data;
    });
}
};
module.exports = BillApi;

And this is the store store.js

var _bill = [];
var BillStore = assign({}, EventEmitter.prototype, {
addChangeListener: function(callback) {
    this.on(CHANGE_EVENT, callback);
},
removeChangeListener: function(callback) {
    this.removeListener(CHANGE_EVENT, callback);
},
emitChange: function() {
    this.emit(CHANGE_EVENT);
},
getAllBill: function() {
    return _bill;
}
});

Dispatcher.register(function(action){
switch(action.actionType){
    case ActionTypes.INITIALISE:
        _bill = action.initialData.bill;
        BillStore.emitChange();
        break;
    default:
        // do nothing
}
});

module.exports = BillStore;

So as I mentioned earlier, when I load data locally using BillApi.getBillLocal() in actions everything works fine. But when I change to BillApi.getBillServer() I get the followind errors in the console...

Warning: Failed propType: Required prop `subscription` was not specified in     `subscriptionsList`. Check the render method of `viewBill`.
Uncaught TypeError: Cannot read property 'period' of undefined

I also added a console.log(data) to BillApi.getBillServer() and I can see that the data is returned from the server. But it is displayed AFTER I get the warnings in the console which I believe may be the issue. Can anyone offer some advice or help me to fix it? Sorry for such a long post.

UPDATE

I made some changes to the api.js file (check here for change and DOM errors plnkr.co/edit/HoXszori3HUAwUOHzPLG ) as it was suggested that the issue is due to how I handle the promise. But it still seems to be the same issue as you can see in the DOM errors.

like image 376
Jose the hose Avatar asked Oct 06 '15 22:10

Jose the hose


People also ask

How do you use flux in ReactJS?

Go to the appDispatcher. js and copy-paste the following code: import { Dispatcher } from "flux"; const dispatcher = new Dispatcher(); export default dispatcher; Here we are importing the Dispatcher from the flux library that we installed, creating a new object and exporting it so that our actions module can use it.

Is flux available in React?

Flux is a pattern for managing how data flows through a React application. As we've seen, the preferred method of working with React components is through passing data from one parent component to it's children components. The Flux pattern makes this model the default method for handling data.

Can I call API in React constructor?

Summary: Calling API in constructor() or componentWillMount() is not a syntax error but increases code complexity and hampers performance. So, to avoid unnecessary re-rendering and code complexity, it's better to call API after render(), i.e componentDidMount().


2 Answers

This is an async issue. Using $.getJSON().then() is not enough. Since it returns a promise object, you have to handle the promise at invocation by doing something like api.getBill().then(function(data) { /*do stuff with data*/ });

I made a CodePen example with the following code:

function searchSpotify(query) {
  return $.getJSON('http://ws.spotify.com/search/1/track.json?q=' + query)
  .then(function(data) {
    return data.tracks;
  });  
}

searchSpotify('donald trump')
.then(function(tracks) {
  tracks.forEach(function(track) {
    console.log(track.name);
  });
});
like image 199
colinterface Avatar answered Oct 02 '22 05:10

colinterface


It looks like from your code that the intended flow is something like:

  • some component fires initialize action,
  • initialize action calls API
  • which waits for results from server (I think here is where things break down: your component render starts before results from server are back),
  • then passes the result to the store,
  • which emits change and
  • triggers a re-render.

In a typical flux setup, I would advise to structure this somewhat different:

  • some component calls API (but does not fire action to dispatcher yet)
  • API does getJSON and waits for server results
  • only after results are received, API triggers the INITIALIZE action with received data
  • store responds to action, and updates itself with results
  • then emits change
  • which triggers re-render

I am not so familiar with jquery, promises and chaining, but I think this would roughly translate into the following changes in your code:

  • controller-view needs a change listener to the store: add a componentDidMount() function that adds an event listener to flux store changes.
  • in controller-view, the event listener triggers a setState() function, which fetches the most recent _bill from the store.
  • move the dispatcher.dispatch() from your actions.js to your api.js (replacing return data);

That way, your component initially should render some 'loading' message, and update as soon as data from server is in.

like image 22
wintvelt Avatar answered Oct 02 '22 04:10

wintvelt