What is the purpose of the React.addons.batchedUpdates API? - javascript

The React v0.12 release announcement included the following:
New Features:
* React.addons.batchedUpdates added to API for hooking into update cycle
However I cannot find any documentation for this API. What is its purpose?
Specifically, any chance that it has an equivalent of Ember.run()?

When responding to synthetic events like onClick and so on, component state changes are batched so lots of calls to this.setState for the same component will only result in one render.
If you are changing state in response to some other async callback (e.g. AJAX or setTimeout) then every call to this.setState will result in a render. You can wrap your work in batchedUpdates(..) to avoid this.
var React = require('react/addons');
var batchedUpdates = React.addons.batchedUpdates;
var request = require('superagent'); // AJAX lib
var req = request('GET', ...).end(function(err, res) {
// invoked when AJAX call is done
batchedUpdates(function(){
.. all setState calls are batched and only one render is done ...
})
});

The default batched update strategy is great for your average website. Sometimes you have extra requirements and need to deviate from that.
The initial reason this was made public is for a requestAnimationFrame batching strategy, which is better for games and sites that need to update often and in many places.
It's just an extensibility point to solve edge case issues.

Related

Is it safe to call sagaMiddleware.run multiple times?

I'm using redux and redux-saga in an application to manage state and asynchronous actions. In order to make my life easier, I wrote a class that acts essentially as a saga manager, with a method that "registers" a saga. This register method forks the new saga and combines it with all other registered sagas using redux-saga/effects/all:
class SagasManager {
public registerSaga = (saga: any) => {
this._sagas.push(fork(saga));
this._combined = all(this._sagas);
}
}
This class is then used by my store to get the _combined saga, supposedly after all sagas are registered:
const store = Redux.createStore(
reducer,
initialState,
compose(Redux.applyMiddleware(sagaMiddleware, otherMiddleware)),
);
sagaMiddleware.run(sagasManager.getSaga());
However, I ran into the problem that depending on circumstances (like import order), this doesn't always work as intended. What was happening was that some of the sagas weren't getting registered before the call to sagaMiddleware.run.
I worked around this by providing a callback on SagasManager:
class SagasManager {
public registerSaga = (saga: any) => {
this._sagas.push(fork(saga));
this._combined = all(this._sagas);
this.onSagaRegister();
}
}
And then the store code can use this as
sagasManager.onSagaRegister = () => sagaMiddleware.run(sagasManager.getSaga());
This seems to work, but I can't find in the docs whether this is safe. I did see that .run returns a Task, which has methods for canceling and the like, but since my problem is only in that awkward time between when the store is constructed and the application is rendered I don't that would be an issue.
Can anyone explain whether this is safe, and if not what a better solution would be?
It may depend on what you mean by "safe". What exactly do you mean by that in this case?
First, here's the source of runSaga itself, and where it gets used by the saga middleware.
Looking inside runSaga, I see:
export function runSaga(options, saga, ...args) {
const iterator = saga(...args)
// skip a bunch of code
const env = {
stdChannel: channel,
dispatch: wrapSagaDispatch(dispatch),
getState,
sagaMonitor,
logError,
onError,
finalizeRunEffect,
}
const task = proc(env, iterator, context, effectId, getMetaInfo(saga), null)
if (sagaMonitor) {
sagaMonitor.effectResolved(effectId, task)
}
return task
}
What I'm getting out of that is that nothing "destructive" will happen when you call runSaga(mySagaFunction). However, if you call runSaga() with the same saga function multiple times, it seems like you'll probably have multiple copies of that saga running, which could result in behavior your app doesn't want.
You may want to try experimenting with this. For example, what happens if you have a counter app, and do this?
function* doIncrement() {
yield take("DO_INCREMENT");
put({type : "INCREMENT"});
}
sagaMiddleware.runSaga(doIncrement);
sagaMiddleware.runSaga(doIncrement);
store.dispatch({type : "DO_INCREMENT"});
console.log(store.getState().counter);
// what's the value?
My guess is that the counter would be 2, because both copies of doIncrement would have responded.
If that sort of behavior is a concern, then you probably want to make sure that prior sagas are canceled.
I actually ran across a recipe for canceling sagas during hot-reloading a while back, and included a version of that in a gist for my own usage. You might want to refer to that for ideas.

Can I call APIs in componentWillMount in React?

I'm working on react for last 1 year. The convention which we follow is make an API call in componentDidMount, fetch the data and setState after the data has come. This will ensure that the component has mounted and setting state will cause a re-render the component but I want to know why we can't setState in componentWillMount or constructor
The official documentation says that :
componentWillMount() is invoked immediately before mounting occurs. It
is called before render(), therefore setting state in this method will
not trigger a re-rendering. Avoid introducing any side-effects or
subscriptions in this method.
it says setting state in this method will not trigger a re-rendering, which I don't want while making an API call. If I'm able to get the data and able to set in the state (assuming API calls are really fast) in componentWillMount or in constructor and data is present in the first render, why would I want a re-render at all?
and if the API call is slow, then setState will be async and componentWillMount has already returned then I'll be able to setState and a re-render should occur.
As a whole, I'm pretty much confused why we shouldn't make API calls in constructor or componentWillMount. Can somebody really help me understand how react works in such case?
1. componentWillMount and re-rendering
Compare this two componentWillMount methods.
One causes additional re-render, one does not
componentWillMount () {
// This will not cause additional re-render
this.setState({ name: 'Andrej '});
}
componentWillMount () {
fetch('http://whatever/profile').then(() => {
// This in the other hand will cause additional rerender,
// since fetch is async and state is set after request completes.
this.setState({ name: 'Andrej '});
})
}
.
.
.
2. Where to invoke API calls?
componentWillMount () {
// Is triggered on server and on client as well.
// Server won't wait for completion though, nor will be able to trigger re-render
// for client.
fetch('...')
}
componentDidMount () {
// Is triggered on client, but never on server.
// This is a good place to invoke API calls.
fetch('...')
}
If you are rendering on server and your component does need data for rendering, you should fetch (and wait for completion) outside of component and pass data thru props and render component to string afterwards.
ComponentWillMount
Now that the props and state are set, we finally enter the realm of Life Cycle methods
That means React expects state to be available as render function will be called next and code can break if any mentioned state variable is missing which may occur in case of ajax.
Constructor
This is the place where you define.
So Calling an ajax will not update the values of any state as ajax is async and constructor will not wait for response. Ideally, you should use constructor to set default/initial values.
Ideally these functions should be pure function, only depending on parameters. Bringing ajax brings side effect to function.
Yes, functions depend on state and using this.setState can bring you such issues (You have set value in state but value is missing in state in next called function).
This makes code fragile. If your API is really fast, you can pass this value as an argument and in your component, check if this arg is available. If yes, initialise you state with it. If not, set it to default. Also, in success function of ajax, you can check for ref of this component. If it exist, component is rendered and you can call its state using setState or any setter(preferred) function.
Also remember, when you say API calls are really fast, your server and processing may be at optimum speed, but you can never be sure with network.
If you need just data only at first run and if you are ok with that. You can setState synchronously via calling a callback.
for eg:
componentWillMount(){
this.setState({
sessionId: sessionId,
}, () => {
if (this.state.hasMoreItems = true) {
this.loadItems() // do what you like here synchronously
}
});
}

How to avoid dispatching in the middle of a dispatch

Within my Flux architected React application I am retrieving data from a store, and would like to create an action to request that information if it does not exist. However I am running into an error where the dispatcher is already dispatching.
My desired code is something like:
getAll: function(options) {
options = options || {};
var key = JSON.stringify(options);
var ratings = _data.ratings[key];
if (!ratings) {
RatingActions.fetchAll(options);
}
return ratings || [];
}
However intermittently fails when the dispatcher is already dispatching an action, with the message Invariant Violation: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.. I am often making requests in response to a change in application state (eg date range). My component where I make the request, in response to a change event from the AppStore has the following:
getStateFromStores: function() {
var dateOptions = {
startDate: AppStore.getStartISOString(),
endDate: AppStore.getEndISOString()
};
return {
ratings: RatingStore.getAll(dateOptions),
};
},
I am aware that event chaining is a Flux antipattern, but I am unsure what architecture is better for retrieving data when it does not yet exist. Currently I am using this terrible hack:
getAll: function(options) {
options = options || {};
var key = JSON.stringify(options);
var ratings = _data.ratings[key];
if (!ratings) {
setTimeout(function() {
if (!RatingActions.dispatcher.isDispatching()) {
RatingActions.fetchAll(options);
}
}, 0);
}
return ratings || [];
},
What would be a better architecture, that avoids event chaining or the dispatcher error? Is this really event chaining? I just want to change the data based on the parameters the application has set.
Thanks!
You can use Flux waitFor() function instead of a setTimeout
For example you have 2 stores registered to the same dispatcher and have one store waitFor the other store to process the action first then the one waiting can update after and dispatch the change event. See Flux docs example
My particular error was occurring because my stores emitted their change event during the action dispatch, while it was still cycling through the listeners. This meant any listeners (ie components) that then triggered an action due to a data change in the store would interrupt the dispatch. I fixed it by emitting the change event after the dispatch had completed.
So this:
this.emit(CHANGE_EVENT);
Became
var self = this;
setTimeout(function() { // Run after dispatcher has finished
self.emit(CHANGE_EVENT);
}, 0);
Still a little hacky (will probably rewrite so doesn't require a setTimeout). Open to solutions that address the architectural problem, rather than this implementation detail.
The reason you get a dispatch in the middle of a previous dispatch, is that your store dispatches an action (invokes an action creator) synchronously in the handler for another action. The dispatcher is technically dispatching until all its registered callbacks have been executed. So, if you dispatch a new action from either of the registered callbacks, you'll get that error.
However, if you do some async work, e.g. make an ajax request, you can still dispatch an action in the ajax callbacks, or the async callback generally. This works, because as soon as the async function has been invoked, it per definition immediately continues the execution of the function and puts the callback on the event queue.
As pointed out by Amida and in the comments of that answer, it's a matter of choice whether to make ajax requests from the store in response to an action, or whether to do it in the store. The key is that a store should only mutate its state in response to an action, not in an ajax/async callback.
In your particular case, this would be exemplified by something like this for your store's registered callback, if you prefer to make the ajax calls from the store:
onGetAll: function(options) {
// ...do some work
request(ajaxOptions) // example for some promise-based ajax lib
.then(function(data) {
getAllSuccessAction(data); // run after dispatch
})
.error(function(data) {
getAllFailedAction(data); // run after dispatch
});
// this will be immediately run during getAllAction dispatch
return this.state[options];
},
onGetAllSuccess: function(data) {
// update state or something and then trigger change event, or whatever
},
onGetAllFailed: function(data) {
// handle failure somehow
}
Or you can just put the ajax call in your action creator and dispatch the "success/failed" actions from there.
you can user the "defer" option in the dispatcher.
In your case it would be like:
RatingActions.fetchAll.defer(options);
In my case, I fetch data through the actions/actions creators. The store is only a dump place that receives the payload of an action.
This means that I would "fetchall" in an action and then pass the result to the store which will do whatever with it and then emit a change event.
Some people consider using stores like me, others think like you.
Some people at Facebook uses "my" approach:
https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51
I think it would probably avoid the dispatch problem treating your stores like this, but I may be wrong.
An interesting discussion is this one: https://groups.google.com/forum/#!topic/reactjs/jBPHH4Q-8Sc
where Jing Chen (Facebook engineer) explains what she thinks about how to use stores.

In Flux what is responsible for direct talking to API

I'm trying to learn Flux, and having watched and read these amazing resources
https://egghead.io/technologies/react
http://facebook.github.io/flux/
https://scotch.io/tutorials/getting-to-know-flux-the-react-js-architecture
I still don't understand which part of Flux architecture (Action, Dispatcher or Store) is responsible for talking to the API, provided that my API is asynchronous, and is able to push data - i.e. I get an event when new data becomes available.
This image suggests that an Action is talking to API, however multiple code examples show that Action is only triggering Dispatcher..
If you look at the role of Actions as informing Stores of updated state data, it seems sensible that API calls that actually get the new data should come before the Action is called (e.g. in the event handlers of the component). However, you may not want API-related logic scattered throughout your Views. To avoid this, a module of ActionCreators is sometimes introduced between View and Action in the above diagram.
Methods for making API calls and handling the returned data by calling appropriate Actions can be collected in ActionCreators, so they will be loosely coupled to your Views. For example,
user clicks login ->
click handler calls ActionCreator.login(), which makes the API call ->
result is passed to Stores by calling Actions ->
Stores update their state accordingly
If your server can push updates through something like websockets, the corresponding event listeners can call methods defined in ActionCreators as well, so all your actions are emitted from one place. Or you could split up user-initiated ActionCreators and server-initiated ActionCreators into separate modules. Either way, I think this achieves a good separation of concerns.
After a couple months working with React + Flux, I've faced the same question and have tried some different approaches.
I've reached the conclusion that the best way is to have the actions deal with data updates, both remote and local:
# COMPONENT
TodoItems = React.createClass
componentDidMount: ->
TodoStore.addListener("CHANGE", #_onChange)
_onChange: ->
#setState {
todos: TodoStore.get()
_onKeyDown: (event) ->
if event.keyCode == ENTER_KEY_CODE
content = event.target.value.trim()
TodoActions.add(content)
render: ->
React.DOM.textarea {onKeyDown: #_onKeyDown}
# ACTIONS
class TodoActions
#add: (content) ->
Dispatcher.handleAction({type: "OPTIMISTIC_TODO_ADD", todo: {content: content}})
APICall.addTodo({content: content})
# STORE
class TodoStore extends EventEmitter
constructor: ->
#todos = [] # this is a nice way of retrieving from localStore
#dispatchToken = #registerToDispatcher()
get: ->
return #todos
registerToDispatcher: ->
Dispatcher.register (payload) =>
type = payload.type
todo = payload.todo
response = payload.response
switch type
when "OPTIMISTIC_TODO_ADD"
#todos.push(todo)
#emit("CHANGE")
when "TODO_ADD"
# act according to server response
#emit("CHANGE") # or whatever you like
#### APICall
class APICall # what can be called an 'action creator'
#addTodo: (todo) ->
response = http.post(todo) # I guess you get the idea
Dispatcher.handleAction({type: "TODO_ADD", response: response})
As you can see, the "juice" is within TodoActions. When a todo gets added, TodoActions.add() can trigger an optimistic UI update via OPTIMISTIC_TODO_ADD that will insert into TodoStore.todos. In parallel it knows that this must be communicated to the server.
An external entity - ApiCall (that can be considered an action creator) - is responsible to deal with the remote part of this action and when you get a response it follows its normal course to TodoStore that can act accordingly.
If you make the stores directly responsible for remote content management you will be adding an extra layer of complexity to it, which made me less confident about the data state at a certain point.
Lets imagine it:
class TodoActions
# TodoActions is `dumb`, only passes data and action types to Dispatcher
#add: (content) ->
Dispatcher.handleAction({type: "TODO_ADD", todo: {content: content}})
# APICall.addTodo({content: content})
class TodoStore extends EventEmitter
# ...
registerToDispatcher: ->
# ...
when "TODO_ADD"
#todos.push(todo)
# now the store has to push it to the server
# which means that it will have to call actions or the API directly = BAD
# lest assume:
APICall.addTodo({content: content})
# it also generates some uncertainty about the nature of the event emit:
# this change can guarantee that data was persisted within the server.
#emit("CHANGE")
The solution I've presented first offers a nice way of doing optimistic updates to the UI, handling errors and displaying loading indications as far as I've experienced.
Reto Schläpfer explains how he approaches this same problem with great clarity:
The smarter way is to call the Web Api directly from an Action Creator and then >make the Api dispatch an event with the request result as a payload. The Store(s) >can choose to listen on those request actions and change their state accordingly.
Before I show some updated code snippets, let me explain why this is superior:
There should be only one channel for all state changes: The Dispatcher. This >makes debugging easy because it just requires a single console.log in the >dispatcher to observe every single state change trigger.
Asynchronously executed callbacks should not leak into Stores. The consequences >of it are just to hard to fully foresee. This leads to elusive bugs. Stores >should only execute synchronous code. Otherwise they are too hard to understand.
Avoiding actions firing other actions makes your app simple. We use the newest >Dispatcher implementation from Facebook that does not allow a new dispatch while >dispatching. It forces you to do things right.
Full article:
http://www.code-experience.com/the-code-experience/

Service call in Fluxxor / React.JS

I'm very very new to react.js and Fluxxor and I haven't done web development for a while. :)
I was wondering where to put server calls (JQuery $.ajax()) in my code?
My actions are only dispatching calls like:
var actions = {
onBlubb: function (data) {
this.dispatch(cmd.BLUBB, data);
},};
Then I have one store which does some changes and calls the emit function to update the view. The whole cycle works fine (view, action, dispatcher, store)
Now I guess I should put my ajax call in my store class. Let's say i call my store "blubbStore".
But I want my store classes to be testable. That means I have to put the ajax call in another store class which basically does the server call and ...
Approach 1) ... triggers a success/failed action. This action is handled in blubbStore
Approach 2) ... stores the service call response in properties. Then blubbStore calls "WaitFor" and reads the data from this "service-caller-store" once the service call is done.
I guess approach 2 is not possible, because the WaitFor does not await asynchronous calls? That means approach 1 would be the solution?
(And the actions should dispatch only messages. right?)
Thanks
In my personal view and experience - it's better to put async call in actions with this logic - image
In this way you can dispatch an event, calling loading screen for example and then, when data is recieved dispatch new change with data.
In the end I believe it's a personal choice, aim for the method that will help you handle code better.

Categories

Resources