Hold off React Render until after request - javascript

I have a component that's being loaded with the initial state:
getInitialState: function() {
return {
panel: "deals",
showExtension: false,
person: {}
};
},
And I have this to find a person:
componentDidMount: function() {
this.findPersonFromBackground();
},
findPersonFromBackground: function() {
chrome.runtime.sendMessage({ action: "findPerson", email: this.props.currentEmail.from_email }, function(person) {
this.setState({person: person});
}.bind(this));
},
So all works fine. If the person is found, I render one thing, and if not, something else.
When the first is found, the view goes from non-found state to found state real fast, since the API calls are pretty quick.
What's the best way to wait to render until that first call comes back?

There's no good way to do what you're asking, which is to keep a component from rendering at all until some asynchronous operation that the component initiates completes; the best you can do is use techniques from other answers to this question to render something slightly different in the case that it's not complete.
However, the problem you're actually trying to solve is preventing the brief flash of alternatively-rendered content if the API request starts and the completes very quickly. If you move the asynchronous operation to the parent of your component, you can ensure that the async operation completes before you ever even render your child.
If the Chrome runtime requests are consistently fast, this may be fine, but in the general case, it's worth considering what the behavior will be if the async operation takes a longer time to complete.

One way you can handle it is with the enum pattern. In this code, this.state.person is either the loading sentinel, the notFound sentinel, or the actual person.
var status = {loading: {}, notFound: {}};
// ...
getInitialState: function(){
return {person: status.loading}
},
fetchPerson: function(){
doFetchPerson(function(person){
if (person) this.setState({person: person})
else this.setState({person: status.notFound})
}.bind(this))
},
render: function(){
var person = this.state.person)
if (person === status.loading) ...
else if (person === status.notFound) ...
else ...
}

Is this more of a design question? You could show a spinner, an empty box, a shapes only preview (like Facebook feed), or nothing at all.
If this is a technical question then return null in render().

maybe you need this project: https://github.com/toplan/react-hold
You can use the placeholder to keep the shape of your component.

Related

React, JS - best practices for setState()

I'm new to JS and React. Went through the documentation given for setState() and found this here:
For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may batch or defer the update until later.
How exactly does it decide which components are to be updated immediately and which are to be deferred?
More precisely, I have something similar to what is given below... Is it a bad idea to have one common setState() outside and another one depending on the conditions? Would it be better performance-wise to do the second option given below even if it means that there'd be a repetition of code?
// First
this.setState({msg: 'Hello!'});
if (someCondition) {
this.setState({
res: true
});
} else {
this.setState({
res: false
});
}
// Second
if (someCondition) {
this.setState({
msg: 'Hello!'
res: true,
});
} else {
this.setState({
msg: 'Hello!'
res: false,
});
}
setState() is async; those two code blocks will run similarly, since react will batch setStates for optimization; So think about the code readability and maintainability; even though if it was different ( which is not ) you should prefer more readability over mere performance difference which is not even measurable; and always keep this in mind when trying new framework or langauage or anything.. premature optimization is the root of all evil
You could just do something like:
this.setState({res: !!someCondition})
and if it is dependent upon a state value:
this.setState(prevState => ({ res: !!prevState.someCondition })
From my opinion first would be better because setState will always recieve full new object not and existing one. So changing one value will be better. I am also beginner but i will prefer first or i will use triple dots i.e '{...this.state}' to get copy of existing state.

Mounting Components in React Native

I am relatively new to JS and RN and I am currently working with an app where I have bumped in to some major issues regarding my handling of Components.
I've tried to run through the following guide: https://facebook.github.io/react/docs/component-specs.html as well as https://facebook.github.io/react/docs/advanced-performance.html but the latter one flies a bit over my head.
However, as I understand: componentWillMount fires whatever piece of code that is within before the render function is executed, and componentWillUnmount erases whatever it sais to forget. Or how can I specify?
My specific problem lies within the fact that I have three functions, one main and within main I have compOne and compTwo, where the two latter are called in the main component when pressing on a certain sub-navigator. This means that I have three instances of getInitialState whereas compOne and compTwo defines basically the same stuff but calls different parts of the server (hence the code is very much the same).
Also this issue resurfaces sometimes when I go between different frames, and return again to my home screen.
In my Home screen I have it like this:
var Home = React.createClass({
getInitialState: function() {
return {
componentSelected: 'One',
userName: "Loading...",
friendFeed: 'Loading...',
loaded: false,
loadedlocal: false,
};
},
componentWillMount: function() {
Method.getFriendFeed(this.props.tokenSupreme)
.then((res) => this.setState({
friendFeed: JSON.parse(res).friendPosts,
loaded: true,
}))
.catch((error) => console.log(error))
.done();
Method.getLocalFeed(this.props.tokenSupreme, )
.then((res) => this.setState({
localFeed: JSON.parse(res).friendPosts,
loadedlocal: true,
}))
.catch((error) => console.log(error))
.done();
},
Where I pass this.state.friedFeed to be a this.props.friendData in one of two components and vice versa for the localFeed.
Picking it up in my CompOne:
var ComponentOne = React.createClass({
getInitialState: function() {
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
return {
dataSource: ds.cloneWithRows(this.props.friendData),
};
},
render: function() {
if (!this.props.loaded) {
return this.renderLoadingView();
} else {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
style={styles.card} />
)
}
},
Followed by the renderRow function etc and the compTwo function is basically identical.
But my question is: How should I go about to unmount the component? If it even is what I want? Another frequently but not consequently occuring issue is the error of null is not an object (evaluating 'prevComponentInstance._currentElement' with reference to the _updateRenderedComponent hence my belief that I should go about some different method in mounting, unmounting and updating my components, or am I wrong?
After some browsing ill add another question to this, which might be the main question... Is it even possible for a RN app to handle mutiple listviews and mulitple fetchers in mutilple scenes?
In most situations, you do not need to be concerned about unmounting the components. When a React component is no longer needed, React in general just forgets about it, including its contents, props, state, etc. componentWillUnmount is typically reserved for things that are in a global state that, once the component is forgotten about would cause problems if they still existed.
The documentation on the page you linked to mentions cleaning up timers as an example. In Javascript, if you set a timer via setTimeout() / setInterval(), those timers exist in the global space. Now imagine you had a component that set a timer to modify some element on screen or potentially try to interact with a component, let's say 30 seconds in the future. But then the user navigates away from the screen/component, and because it is no longer on screen React forgets about it. However, that timer is still running, and may cause errors if it fires and it can't interact with that now-trashed component. componentWillUnmount gives you a chance to clear out that timer so weird side effects don't occur when it fires to interact with elements that no longer exist.
In your case you probably don't have anything that needs cleanup, as far as I can tell. You might want to clarify your question because you don't say what the trouble behavior is that you're seeing, but note also that getInitialState is only called the first time a component is created, and won't get called if only props change. So if the friendData is changing but the component stays on the screen, you will need to update your ds via a componentWillReceiveProps.
To your last question, yes it is certainly possible for React to handle multiple ListViews/fetches/etc.

React.js - clean way to differentiate loading/empty states in Render

I have my component:
getInitialState() {
return {
items: []
};
},
componentDidMount() {
// make remote call to fetch `items`
this.setState({
items: itemsFromServer
})
},
render(){
if(!this.state.items.length){
// show empty state
}
// output items
}
Extremely contrived/sandboxed, but this is the general idea. When you first load this component, you see a flash of the "empty state" HTML, as the server hasn't yet returned any data.
Has anyone got an approach/a React Way™ of handling whether there is actually no data vs. showing a loading state?
I've just been rendering a empty span element but you could just as easily render a CSS spinner of some kind to show it's loading.
if(!this.state.items.length){
return(<div class="spinner-loader">Loading…</div>);
}
http://www.css-spinners.com/
You may also want to consider what happens if your response comes back with no results. I would use (this.state.items === null) to indicate that you are waiting for results and an empty array/collection (!this.state.items.length) to indicate that no results were returned.

Event like componentDidUpdate, but fired only once

I have some components that should do some work as soon as their data has arrived and rendered for the first time, but not for future rerenderings. For example: Comments are loaded and rendered, now 1. load social media libraries and 2. load some Google Analytics.
Right now I'm doing it like that:
componentDidUpdate: function (prevProps, prevState) {
if (this.hasAlreadyUpdatedOnce) {
// ... do some stuff...
} else {
// ... do some stuff that should happen only once...
// 1. load social media libraries
// 2. load some Google Analytics
this.hasAlreadyUpdatedOnce = true;
}
}
But I'm asking myself if there's a more elegant way than setting a property like that.
Assuming you're responding to a state change, you should pass a callback as the second argument to setState.
componentDidMount: function(){
ajaxyThing(function(data){
this.setState({data: data}, function(){
// this.state is updated, the component has rerendered
// and the dom is current
});
}.bind(this));
}
You want componentDidMount(). Details here.
Have you tried updating state once the ajax call has finished?
Or you can return false for componentShouldUpdate and once the ajax call promise has resolved call forceUpdate.
I can't give you a definitive answer because I don't know if your ajax call is in the parent or child component but either way you should be able to leverage shouldComponentUpdate() to accomplish your goals. If you really don't ever want to update your component after the ajax call comes in then you can do something like this:
shouldComponentUpdate() {
return false;
}
and then when your ajax call comes back just run this.forceUpdate(). returning false will make it so that your component never updates unless you run this.forceUpdate(). However this is not the best solution to the problem I just can't give a better one without more information.
The React docs have a good example on how to handle this using isMounted().
isMounted() returns true if the component is rendered into the DOM,
false otherwise. You can use this method to guard asynchronous calls
to setState() or forceUpdate().
Example
First, initialize your state variables in `getInitialState()':
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
}
}
In componentDidMount() make the ajax call ($.get in this case) then re-set the state variables:
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
}

React js - Disable render of a component in a mixin

I'm trying to develop a React mixin to check the user access level before rendering the component.
If the user doesn't have the permission to see the component, I would like to disable the rendering of the component.
I've been looking for something build in react to handle this but found nothing, so I did that:
var AuthentLevelMixin = {
componentWillMount: function() {
if(!Auth.check()) {
// Disable component render method
this.render = function () {
return false;
}
}
}
}
It works as expected but I feel like it's the "dirty way".
So my question is: what is the "React way" for doing the same as this snippet ?
For a mixin this is about the best you can do. It's just a simple early return in render.
var AuthentLevelMixin {
isAuthenticated: function(){
return Auth.check();
}
};
var C = React.createClass({
mixins: [AuthentLevelMixin],
render: function(){
if (!this.isAuthenticated()) return <div />;
return (
<div>...</div>
);
}
});
If you decide to go with your initial strategy (I don't recommend it), it just needs to be modified slightly:
// more explicit names are important for dirty code
var PreventRenderUnlessAuthMixin = {
componentWillMount: function() {
this._originalRender = this.render;
this._setRenderMethod();
},
componentWillUpdate: function(){
this._setRenderMethod();
}.
_emptyRender: function () {
return <span />;
},
_setRenderMethod: function(){
this.render = Auth.check() ? this._originalRender : this._emptyRender;
}
}
If you want to handle the authorization inside your mixin without adding logic to your component you are doing it the right way. BUT: Every component implementing this mixin should then be aware of what happens within this mixin. If the result you expect is, that nothing is rendered, then you are perfectly right with what you are doing. So if your way is resulting in simplicity it is the React-Way. And in my Opinion this is the case.
In the componentWillMount lifecycle event you will capture the moment right before rendering - which is a great time to prevent rendering. So I really dont see anything speaking against your code.
EDIT:
aproach of defining: "react way"
Once you have the same input resulting in the same output every time your code becomes predictable. With your code being predictable you achieve simplicity. These are terms used by Pete Hunt to describe the intentions of React. So therefor if you stay predictable and in result achieving simplicity you are doing it the react way.
In case of the above mixin both these rules apply and is therefor the "react way" in the definition I have provided above.
My advice here would be to not use a mixin. The best way to clean up your component is to remove this logic from the component, and simply not render the component based on the result of checking Auth.
The problem with this is that you have a component that is no longer consistent, because it depends on something other than its props. This doesn't really do much other than push the problem upwards, but it does allow you to have one more pure component.
I can see why the mixin is attractive though, so here's a simpler way of doing what you need that doesn't involve dynamically swapping the render method:
var PreventRenderUnlessAuthMixin = {
componentWillMount: function () {
var oldRender = this.render;
this.render = function () {
return Auth.check() ? this.render() : <div />
}.bind(this);
}
}

Categories

Resources