How to have multiple instances of getInitialState in Reactjs? - javascript

This is my code in React. I have multiple instances of getInitialState for different functions. However, I am finding that there may be a problem with getting them all to run this way.
getInitialState: function(){
if (localStorage.getItem('something'))
{return {signedin: true}}
else {return {signedin: false}}
},
getInitialState:function(){
return {error: true}
},
getInitialState:function(){
return {fliphouse: false}
},
To explain, the second instance of getInitialState, as is, does not work. That is, unless, I flip the order of the second and third getInitialState. When I do that, the code runs as it should. But I get the feeling React is trying to tell me something.
Is this normally the way to organize React code within a component?

You can define them all in the same object:
getInitialState() {
return {
signedIn: !!localStorage.getItem('something'),
error: false,
fliphouse: false
};
}
You can change them individually anywhere in the component (except in the render() function), e.g.:
this.setState({ error: true }) // causes a new render

This is not the correct way to put the initial state of the component. Initial state function is called only once when you component is about to mount; over here you set the state of the component(what variables or object properties you need) and then update them accordingly using setState in other methods like componentDidMount etc..
So in your question I would say put all 3 variables signedIn, error and fliphouse in the state object and then update this object. So a single initialState function would do.

Related

Best way to run a function when page refresh

I am trying to call a function when the page is refreshed. I adding a state if the page is rendered with the data I got from my backend end but I get an warning message "Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state." Even though it works fine (except with the warning message), I dont think this is the best, most efficient way to do it?
If this is the best, most efficient way, how do I fix the waring message?
function Demo() {
constructor(){
this.state = {
username: "unknown",
rendered: false,
}
this.renderUserProfile = this.renderUserProfile.bind(this);
}
update(){
//code to retrieve data from backend node.js *
this.setState({ username: data });
this.setState({ rendered: true });
}
render(){
if (!this.state.rendered) {
this.update();
}
return (<p>demo</p>)
}
}
Thank you for your help!
Do never change state inside render, because every state (or prop) change will call render again. That is what the warning is telling you: you risk having infinite loops.
There is no need of a state param for "rendered", because your component will call render anyway as soon as this.setState({username: data}); executes. If you want something to happen then, add it in update just after the setState line.
Now let's imagine that you still really want it. If you don't want your component to render when the rendered state changes, then just don't use the React Component state, but any standard class attribute:
class MyComponent extends React.Component {
rendered = false
...
render() {
this.rendered = true
....
}
}
Just be aware that this looks super wrong (and useless) since it tries to go around what the React framework is good at.
Finally, from this code there is no way to know how you intend you have new data coming in. If it is an Ajax call, then you will call this.update with that data in the callback of your Ajax call - certainly not in render.

React - updating state during render produces errors

I'm new to React and am trying to update the state of a parent component from the child everytime an onChange action happens. The onchange action comes from an input box that when letters are typed it updates the state of searchInputVal with the value of what has been typed. I have a parent <App/> component with the following properties and states here:
updateSampleFilteredState(filteredSamples) {
this.setState({
samples: filteredSamples
});
},
getInitialState () {
return {
samples:allSamples,
searchInputVal:""
}}
I pass the properties and states down to a child component here:
updateNewSampleState(filteredSamples){
return (
this.props.updateSampleFilteredState(filteredSamples)
)
}
render() {
const filteredSamples = this.props.samples.filter(sample => {
return sample.sampleFamily.toLowerCase().indexOf(this.props.searchInputVal.toLowerCase()) !== -1;
});
this.updateNewSampleState(filteredSamples);
return <div className="samples-container-inner-styling">
{
filteredSamples.map((sample) => {
return (...
Before I added the line this.updateNewSampleState(filteredSamples); the child component would render out the filtering just fine but obviously not update the state of sample with the new filtered state. When I the line this.updateNewSampleState(filteredSamples); to execute the function in the component to set the new state I get a list of re-occuring errors that eventually make my app crash. The errors say something about an anti pattern. I'm not sure how else to update the state?
You should't be updating the state from the render function, and you are facing the reason why that's a bad way to do things. Every time you call the setState the component re-renders, so if you call it inside the render function it will be called again and so on... You should ask yourself why are you calling that function there. I guess you could just do it in the onChange function you are using for the input.
As already mentioned by #César, setting the state in the renderer doesn't make sense, since setting the state triggers a rerender of the component, so you basically get something like an infinite render loop.
Given that you are computing filteredSamples only from the props, you could compute that state in the constructor:
The constructor is the right place to initialize state.
However, note the following when deriving state from props in the constructor:
It's okay to initialize state based on props if you know what you're doing. [...]
Beware of this pattern, as it effectively "forks" the props and can lead to bugs. Instead of syncing props to state, you often want to lift the state up.
If you "fork" props by using them for state, you might also want to implement componentWillReceiveProps(nextProps) to keep the state up-to-date with them. But lifting state up is often easier and less bug-prone.

React componentDidUpdate method won't fire on inherited props change if connected to a store that didn't change

I want my component know if some library is already loaded. To know that from any context i connect it to the "library" reducer of my store to my component.
I also pass it a configuration object this.props.dataObject from the parent where the component has been called. Like this:
class GoogleButton extends Component {
render() {
if (this.props.libraries.google) {
return <a id='sharePost' className='google_icon'></a>
} else {
return null
}
}
componentDidUpdate() {
gapi.interactivepost.render('sharePost', this.props.dataObject)
}
}
function mapStateToProps(state) {
return { libraries: state.libraries }
}
export default connect(mapStateToProps)(GoogleButton)
The reducer that handles the libraries state is like this:
let newState = {...state}
newState[action.libraryName] = action.state
return newState
When I change the library state componentDidUpdate works. The problem is when i change the prop inherited by the parent this.props.dataObject. In that case is where componentDidUpdate wont fire. If i remove the connect from the component it works as espected. I'm missing something here?
Most likely some of your props are mutated outside the component.
For example, you might be rendering your component like this:
class Parent extends Component {
constructor() {
super()
this.state = { libraries: {} }
}
handleClick() {
// MUTATION!
this.state.libraries.google = true
// Normally this forces to update component anyway,
// but React Redux will assume you never mutate
// for performance reasons.
this.setState({ libraries: this.state.libraries })
}
render() {
return (
<div onClick={() => this.handleClick()}>
<GoogleButton libraries={this.state.libraries} />
</div>
)
}
}
Because Redux apps deal with immutable data, connect() uses shallow equality check for its props to avoid unnecessary re-renders. However, this won’t work if you use mutation in your app.
You have two options:
Don’t Mutate Anything
This is the best option. For example, instead of something like
handleClick() {
this.state.libraries.google = true
this.setState({ libraries: this.state.libraries })
}
you can write
handleClick() {
this.setState({
libraries: {
...this.state.libraries,
google: true
}
})
}
This way we are creating a new object so connect() wouldn’t ignore the changed reference. (I’m using the object spread syntax in this snippet.)
Disable Performance Optimizations
A worse alternative is to completely disable performance optimizations made by connect(). Then your props would update even if you mutate them in the parent, but your app will be slower. To do this, replace
export default connect(mapStateToProps)(GoogleButton)
with
export default connect(mapStateToProps, null, null, { pure: false })(GoogleButton)
Don’t do this unless absolutely necessary.
I solved it. I'm not 100% sure that this is accurate, but I will explain. If im wrong with something, please correct me.
I keep thinking about the shallow equality check that Dan said in his answer. The problem was there.
I was passing down an object from the parent and the nested elements of that object were the ones that changed. The object remain the same. So with the shallow equality check that connect brings the component will never update.
My solution was in the parent use Object.assign({}, dataObject) when I pass down the prop so I make another different object. Now shallow equality check could compare it and determinate that the props have changed and there before update the component.
i had same problem and i used object.assign for create new state but i use combineReducer and it cause multi level state ,in my case i pass whole state as props to component so shallow equality check can not detect my state change so componentDidUpdate didnot call,it is important to pass state in level it change when using combine reducer
in my case i pass it like this
const MapStateToProps=(state)=>{
return {
reportConfig:state.ReportReducer
}
};
and my state tree is like this
{
ReportReducer: {
reportConfig: {
reportDateFilter: 'this week',
reportType: null,
reportShopId: null,
updateShop: true
}
}
}
and in my reducer and return it like this as ReportReducer
export default combineReducers({reportConfig});
and my root reducer is like this
const rootReducer =combineReducers({ReportReducer});
const store = createStore(rootReducer ,{},enhancer);
Another option that you can use is to make a deep copy of the inherit prop this.props.dataObject on the child component, this in order for the componentDidUpdate to 'catch' the updated prop, you could use:
dataObject={JSON.parse(JSON.stringify(valueToPass))}
Use this where you are passing the prop from the parent component, this works for me in a similar problem (This applies when you don't have any function inside the prop).
I had this exact same problem with Components I used from an external library.
So I didn't had the option to modify the inherited property.
I only needed a part of the inherited property object (will use dataObject for simplicity). Solved it by adding it to the mapStateToProps function:
function mapStateToProps(state, ownProps) {
return { libraries: state.libraries, neededValue: ownProps.dataObject.value }
}
By which a shallow compare is enough to notice a value change. So use this.props.neededValue iso this.props.dataObject.value in the render() function.

React - this.state is empty in render when getInitialState is properly defined

I'll try my best to explain the scenario, as making a fiddle of the smallest possible case might be hard. Essentially, I have a SPA using React-Router. I'm currently getting a strange behavior in specifically one version of Firefox (31.4esr).
I have two sidebar icons which trigger a change in routes, navigating to a new page. On occasion when I switch quickly between them, I'm getting an error that this.state.list is undefined(this is a list that I populate a dropdown with).
The issue is, upon debugging, console.log(this.state) is returning an empty object just before the call (that errors) to this.state.list happens in my render method. However, I have list defined in getInitialState (along with a bunch of other state variables) and so this.state definitely shouldn't be empty.
The only thing I could think of that would be causing this is if due to the quick switching there is some confusion with mounting/unmounting of components and my component still thinks it is mounted, so skips the getInitialState and goes ahead and tries to render. Either that or some bug in React-Router.
Any thoughts? Thanks in advance for the help!
Nick
P.S I should reiterate this also only occurs very rarely during quick switching, and normally the componentDidMount -> getInitialState -> render occurs as expected, so it is not simply an error in how my getInitialState is written etc.
Edit: Using React 0.13.3 and React router 0.13.3
Edit 2: Here is the stripped down version of the lifecycle methods, very basic.
getInitialState: function() {
return { list: listStore.getList("myList") || [] }
},
render: function() {
var newList = [];
//this is the line that errors with this.state.list is undefined
this.state.list.forEach(function(listItem) {
...
}
return (
<div>
<OtherComponent newList={newList} />
</div>
)
};
When putting console.log in componentWillMount (just attaches store listeners), getInitialState, and render, I get output like this when the error occurs:
"Setting initial state"
"2,3" //This is this.state.list in componentWillMount
"2,3" //This is this.state.list in initial Render
Object { } //This is this.state the next time it gets called in render :S.
you will have to use mixin like this:
var state = {
getInitialState: function() {
return { list: listStore.getList("myList") || [] }
}
}
var nameRouter = React.createClass({
mixins : [state],
// more content
})
this is because react-router ignore the getInitialState that was definite

How to make a react component that can be rendered with no data and later rendered with data?

I have a situation that I'm sure is common and I just haven't learned the react way for accomplishing it. Let's say I have this:
var appView = new React.createClass({
render: function() {
return (
<div>
<SomeSubview/>
</div>
)
}
});
React.render(
React.createElement(appView),
$('#app').get(0)
);
My question is how should I create the SomeSubView react component so that it can render properly without any data, and then later render showing some data when the data is available. I have pub/sub system set up, so I'd like to be able to subscribe to an event and get the data to SomeSubView that way. SomeSubView might look something like this:
SomeSubView = new React.createClass({
componentDidMount: function() {
pubsub.subscribe({
callback: function() {
// something the sets the state or a prop of this component
}
});
},
render: function() {
// something that renders properly when
// there is no data and renders the data when there is data
return (
<div></div>
)
}
});
I can't tell if this a case for state or props on the react component? I don't know if it's best practice to put conditionals in the render function?
In your SomeSubView just check if data is available in your render function, but before returning the markup.
Like this:
SomeSubView = new React.createClass({
componentDidMount: function() {
pubsub.subscribe({
callback: function() {
// something the sets the state or a prop of this component
}
});
},
render: function() {
// something that renders properly when
if( this.state.data.length > 0 ){
var data = <li>{this.state.data}</li>;
}
return (
<div>{data}</div>
)
}
});
If the variable data is not set, React will simply pass over it as non-existent.
You can of course also use .map() on your state data to loop out markup just like in most render examples.
You have to use state like user3728205 said, especifically setState().
setState(function|object nextState[, function callback])
Merges nextState with the current state. This is the primary method
you use to trigger UI updates from event handlers and server request
callbacks.
The first argument can be an object (containing zero or more keys to
update) or a function (of state and props) that returns an object
containing keys to update.
Here is the simple object usage...
setState({mykey: 'my new value'});
What this says is that "whenever" you update your state via setState, React will execute the method render again for you. So, you should put yor display logic based on the state, when it changes the view displayed will change too.
I say "whenever" because React doesn't fire re-render immediatily, but creates a pending state transition.
NEVER mutate this.state directly, as calling setState() afterwards may
replace the mutation you made. Treat this.state as if it were
immutable.
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
There is no guarantee of synchronous operation of calls to setState
and calls may be batched for performance gains.
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
For more information about the magic of React you should read this.
https://facebook.github.io/react/docs/reconciliation.html
A simple example that maybe can help.
And i recommend read the flux architecture that is very easy to understand and implement (is about utilizing a unidirectional data flow), and you have implementations like Fluxxor that facilitates the use of flux. This is for your pubsub part.

Categories

Resources