Good Morning All!
I've a react Component (a View) that's dependent on a Store which is in turn dependent on having some state pulled from a round-trip to the server.
What I'm looking to understand is if there's a common pattern to solve for initializing the Store's state.
Right now I'm thinking I'd do something like:
var SomeView = React.createClass({
componentWillMount: function() {
SomeStore.addChangeListener(this._onChange);
// Go and tell this thing we want to initiliaze our
// state ahead of time. My worry here is obviously
// that when state is updated this fires again so I'd
// need to have some knowledge that the store has been
// initialized which seems very (very) kludgey
SomeActions.init();
},
render: function() {
// Here i'd want to see if I had items available for
// rendering. If I didn't I'd drop on a loading dialog
// or if I did I could render the detail.
},
_onChange: function() {
// this.setState...
}
});
var SomeActions = {
init: function() {
AppDispatcher.dispatch({
actionType: SomeConstants.INIT
});
}
};
var SomeStore = assign({}, EventEmitter.prototype, {
init: function() {
$.get('/round/trip', function(data) {
this.emitChange();
}).bind(this);
}
emitChange: function() {
this.emit(CHANGE_EVENT);
},
addChangeListener: function(callback) {
this.on(CHANGE_EVENT, callback);
}
});
AppDispatcher.register(function(action) {
switch(action.actionType) {
case SomeConstants.INIT:
SomeStore.init()
break;
default:
}
});
I am absolutely positive there must be a better way.
My worry here is obviously that when state is updated this fires again
componentWillMount fires once component injected to DOM, state updates will not fire this method. However, if you remove component from DOM (for example: not rendering it in parent component based on some condition) and render it later, the method will be fired again. So init will be called multiple times on the store.
I believe you should move http request code to Web API module and fire an action to the API from componentWillMount method, the API will then trigger the store and fire change event on the component, which will update the state and re-render. This is how Flux works.
If you need to get data only once and you know your component is going to be removed from/added to DOM multiple times, you should put a call to the api into upper component in the tree (component that represents an entry point to the widget or something).
I recommend to check Component Container or Higher-order Components pattern, which basically defines a thin wrapper component as a data layer over the view component. Thus you can completely separate your views from data layer and it works good.
You may also want to check another approach, coming from ClojureScript's Om, with a single immutable state. This simplifies everything even more and actually the best way I've found for my self to build apps with React. I've create a starter kit for it, there's a good explanation of main concepts in the readme.
Related
I was wondering is there any difference between these two ways of defining React component attributes:
var something = React.createClass({
SSEStream: new EventSource("/stream/"),
componentDidMount: function() {
this.SSEStream.addEventListener("message", function(msg) {
// do something
}.bind(this));
});
var something = React.createClass({
componentDidMount: function() {
this.SSEStream = new EventSource("/stream/");
this.SSEStream.addEventListener("message", function(msg) {
// do something
}.bind(this));
}
});
Note the difference of how the React component attribute SSEStream was defined. My guess is that in the second example attribute is being recreated every time component is re-rendered whereas in the first it is created only once and therefore the first way should be preferred.
So the question is, will there be even the slightest difference between the two?
The difference between the two is as follows:
The first instantiates and sets a single EventSource at the time the component is declared, which is shared between each instance of the component.
On the other hand, the second creates a separate EventSource for each instance of the component, when the callback is fired.
Assuming that you want multiple instances of the component to be independent of one another, then I guess that the second option is what you want.
By the way, the componentDidMount callback is typically only run once in the life-cycle of the component, when the component is first mounted, so this has nothing to do with re-renders.
I know that redux is great for handling the global state of an application, and when that state is updated to reflect that in the view. However, is it possible to use it on a react component that shouldn't re-render? I have a map which uses leaflet and rather than re-render and plot the data, I want it to plot the data without re-rendering.
Does redux seem like a good choice? If so, where would I handle api calls as I was told it should not be done in the reducer.
Currently my app consists of a nav bar, a fullscreen map and a search menu which is populated from an api request. The idea is that when a search is selected it populates data onto the map.
Can you share how you're using Leaflet and React without Redux?
Essentially, React components will always re-render if their state changes, so you can either have a React component that updates on every change, or a Redux-connected component that updates on every subscribed state change. Doesn't make a difference either way.
If you want React/Redux to be 'aware' of your leaflet widget, the only way to do that is to have it re-render on change. Bear in mind that a 'render' function doesn't just throw away and rebuild that part of the DOM, so re-rendering on every change won't cause your leaflet component to be destroyed and rebuilt.
You could connect the Redux dispatcher but not the state, so that you can publish changes to your server through redux, but not have the state connected. This doesn't seem like the ideal approach to use though.
Another approach is to have a 'persistedMapCoordinates' property that is only set when the user confirms their selection, but not on every change. That way the re-render only happens when they lock in their change, not on every small adjustment.
For doing the API calls, you'll want to use redux thunk and middleware. There is tons of info about this available online :)
If your component doesn't re-render, then I'd suggest not complicating it with Redux. It sounds like you just need a component that manages its own rendering.
var MyMapComponent = React.createClass({
componentWillMount: function() {
fetch('/some/data')
.then(this.update);
},
update: function(data) {
// calling setState will trigger shouldComponentUpdate
this.setState({ data: data });
},
loadMap: function(container) {
// calling setState will trigger shouldComponentUpdate
this.setState({
map: L.map(container)
});
},
shouldComponentUpdate: function(nextProps, nextState) {
var map = this.state.map;
var data = this.state.data;
// make updates to map here
// prevent react from re-rendering this component
return false;
},
render: function() {
// pass a reference to the dom node out to loadMap
return (
<div ref={this.loadMap}></div>
);
}
});
I'm using MutationObserver in an AngularJS SPA to detect changes in the DOM everytime the ng-view is updated via user navigation. This is better explained here http://www.soasta.com/blog/angularjs-real-user-monitoring-single-page-applications/
This is the function which is fired with every soft navigation (within the SPA)
function initMutationObserver() {
if (window.MutationObserver) {
// select the target node
var target = document.querySelector('.sport');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.info(mutation.type);
});
console.info('**** DOM changes ****')
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true, subtree: true};
// pass in the target node, as well as the observer options
observer.observe(target, config);
}
};
That is, everytime the user navigates to a new page within the SPA this observer detects changes in the DOM.
Problem is, for every navigation this observer gets fired many times and not only one, so I donĀ“t really know when this is over. Has anyone any idea on how to do that? Is there a way with the MutationObserver that I can be sure the DOM has alredy finish updating and I can call the loading over?
Many thanks!
https://docs.angularjs.org/api/ngRoute/provider/$routeProvider
$routeProvider.when('some/path',{
resolve: {
data1: function(){
//do your asynch call (promise) returning your data
},
data2: function(){
//do your asynch call (promise) returning your data
},
controller: function(data1,data2){
//data1 and data2 will be resolved by when controller is instantiated
}
}
})
or if you want to use ui.router
https://github.com/angular-ui/ui-router/wiki#resolve
quite similar to the previous case
Once your mutation has occurred, you should destroy the observer:
observer.disconnect();
You may have your NodeList complete or something else...
Imagine it as a camera that is waiting to see when all those DOM events happen. (You may not need all of them, by the way).
But it is important to cease its activity.
I am learn reactjs flux pattern from the link below
https://scotch.io/tutorials/getting-to-know-flux-the-react-js-architecture
I get completely confused and lost with this following bit, when will the following code ever been trigger or used? i do not see any where in the app where the this._onChange will be trigger... please help and explain any suggestion is appreciated as i am start to learn.
// Listen for changes
componentDidMount: function() {
ShoeStore.addChangeListener(this._onChange);
},
// Unbind change listener
componentWillUnmount: function() {
ShoesStore.removeChangeListener(this._onChange);
},
in the store, does it means in order to trigger the update need to run ShoesStore.emitChange()?
// Emit Change event
emitChange: function() {
this.emit('change');
},
// Add change listener
addChangeListener: function(callback) {
this.on('change', callback);
},
// Remove change listener
removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
In typical Flux app, your store emit change (as EventEmitter) and _onChange is invoked because it has been assigned by addChangeListner, which needs to be removed afterwards otherwise it cause memory leak. componentDidMount and componentWillUnmount is invoked at certain life cycle phases, as method names say, just after the component is mounted, and just before unmounted.
Correct answer: (summary from BinaryMuse)
When you're creating a store you'll usually call emitChange() yourself (it's not usually automatic).
I am new to reactjs. I want communication to happen between two independent components.
These components are not having any parent child relationship.
I found this piece of code.
I dont know how to use it
https://medium.com/react-zine/how-to-communicate-between-components-in-react-cc1ee986523a
From the docs:
For communication between two components that don't have a
parent-child relationship, you can set up your own global event
system. Subscribe to events in componentDidMount(), unsubscribe in
componentWillUnmount(), and call setState() when you receive an event.
Flux pattern is one of the possible ways to arrange this.
We use the PubSub pattern to attach global events, but as the docs say you can use many different arrangements.
Example using PubSub
Receiver component:
componentDidMount: function() {
this.token = PubSub.subscribe('MY TOPIC', this.subscriber)
},
componentWillUnmount: function() {
PubSub.unsubscribe(this.token)
},
subscriber: function(msg, data) {
console.log(msg, data)
// set state etc...
})
Emitter:
PubSub.publish('MY TOPIC', 'hello world!')