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.
Related
I have a module called LegalModule, there are three components that subscribe to the same module, basically:
Both components have their own folder and each have an index.js file where they bootstrap like:
angular.module('LegalModule')
.component('person', require('person.component.js')
.controller('PersonController', require('person.controller.js');
and another file like
var component = {
templateUrl: 'person-tamplate.html',
controller: 'PersonController',
bindings: {info: '<'}
}
module.exports = component;
Then in that controller i have something like :
var controller = ['PersonRepository','$stateParams', function(PersonRepository, $stateParams)
{
var vm = this;
//other code
function Save(){
//code that saved
}
function onSuccess(){
//Let another component know this happened and call its refresh function.
}
}];
Other component / controller
angular.module('LegalModule')
.component('buildings', require('buildings.component.js')
.controller('BuildingController', require('buildings.controller.js');
and the component
var component = {
templateUrl: 'building-template.html'
controller: 'BuildingController'
}
Controller
var controller = ['BuildingReader',function(BuildingReader){
function refreshBuildings(){
//this needs to be called on success of the save of the Person Repository
}
}];
On the main tamplate:
<div class="LegalFacilities">
<person></person>
<buildings></buildings>
</div>
So i am new to components and i am not sure how to make in a way that when something is saved in the person controller, on it's success, that it can trigger the refresh function in the building controller to fire.
I really do not want to use $scope or anything like that , there is gotta be a cleaner way?. (not sure but i would appreciate any inputs).
Since you have two components that are not on the same DOM element, your methods of communicating between them are more limited. You still have several ways that you can do it:
onSuccess() emits an event on the $rootScope and all interested controllers listen for that event (just make sure to unsubscribe to the event on $destroy).
Create one or more services that contain the all the non-UI shared application state. All controllers that need access to state inject the service that contains that state. And controllers can also $watch a variable on the service to be notified when something changes and something needs to be refreshed.
Pass state around using the parent scope. Ie- each child scope declares a scope variable that is bound to the same variable in the parent scope. And if the state changes in one of the child scopes, the $digest cycle will ensure that the state is propagated to the other child scope.
In general, my preference is #2. The reason is that this keeps a clear separation between application state and UI state. And it becomes very easy to ensure that all parts of your application can share bits that they need to.
In your case, since you need to notify that an action happened, you can trigger this through changing a successHash number (an opaque number that just gets incremented on every save such that all watchers are notified).
Edit: a very simple example of sharing state using services.
angular.module('mymod').service('myService', function() {
this.val = 9;
});
angular.module('mymod').directive('dir1', function(myService, scope) {
scope.doSomething().then(res => myService.val = res);
});
angular.module('mymod').directive('dir2', function(myService, scope) {
scope.$watch(() => myService.val, () => console.log(`It happened! ${myService.val});
});
I have this component in my app:
<actions-bar actions="$ctrl.actionsBarData"></actions-bar>
Here is the controller / component definition:
.component('actionsBar', {
controller: 'actionsBarController',
bindings: {
actions: '<'
}
})
.controller('actionsBarController', function() {
var vm = this;
vm.$onInit = function() {
console.log(vm.actions);
setTimeout(function() {
console.log(vm.actions);
}, 500);
};
});
Take note of the two console.log statements within $onInit. In the browser the first logging statement prints undefined. The second statement, wrapped in setTimeout correctly prints out the this.actions object.
According to the Angular docs:
$onInit() - Called on each controller after all the controllers on an
element have been constructed and had their bindings initialized
If the bindings have been initialized, why is the data not immediately available? Why must I set a delay of 500ms before it is available?
If $onInit isn't the solution here, how can I reliably access the bindings data within my controller? It appears that this is correct lifecycle hook to use, and none of the other hooks appear to be what I want in this case.
This is because while the bindings have been initialized in the controller, they haven't necessarily been initialized where they are coming from. If you are populating your binding with an async call, the object is going to be undefined or empty.
$onChanges allows you to inspect changes that happen to your bindings.
vm.$onChanges = function (changesObj) {
console.log(changesObj.actions);
}
If what I described above is happening, this will fix it for you.
EDIT: a word
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 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).
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.