React: Can I pass component default state values via props? - javascript

For eample:
<Counter start="10">
...
export default class Counter extends React.Component {
constructor(props) {
super();
this.state = {
start: props.start
};
}
}
I googled this question and I got an idea that the answers I found are outdated
The questions on StackOVerflow
ReactJS: Why is passing the component initial state a prop an anti-pattern?
ReactJs: How to pass the initial state while rendering a component?
But I found this post in React blog: React v0.13.0 Beta 1
And in that post author does exactly what I want, as I understand getDefaultProps is deprecated now.
So the question is: Is passing state through props still an anti-pattern?

IMHO 'yes' because you give the impression that changing the prop value would change the behaviour of the component which won't happen. Your component would behave exactly the same when I change the start param.
Impressions are cool but needs are real. At times when I need this type of behaviour I simply name my prop accordingly like initialFoo or defaultBar.

Related

How this.setState() (react.Component method) has access of his child's class state?

Actually, I am learning to React JS, But I have confusion that how can a parent's class method has access to his child's class state. I searched a lot on this topic but in object-oriented programming parent class hasn't access to his child's properties (state). But in react js how setState() has to access to his child's class properties (state). It can be a stupid question please tell me how it happens?
Don't worry, this is a great question.
Props are passed by reference!
Here are some great answers from a StackOverflow post that answer your question with more specificity.
In reactjs, are props pass by value or pass by reference?
I think some potential nuance I can offer is that React emphasizes composition over inheritance. You should think of it as your parent component being composed of the child components. While the child components are given access to the parent state, they aren't inheriting it.
Understanding the Parent Child structure through the Component API may help, should you want to clear any confusion. Since you have mentioned Class Components, let me illustrate an example with the same.
You may send props from a Parent to a child through defining a property within the JSX insertion -
<Child uniqueProp="Text sent to Child" />
Conversely, it is tricky to access Child data in a Parent component. You'll have to follow these steps -
Define a callback function in the Parent Component to get data from the Child Component.
Pass the callback function in the Parent Component as a prop to the Child Component.
The Child Component calls the Parent callback function using props.
Example -
Parent Component
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Original Text"
}
}
handleCallback = (value) => {
this.setState({ text: value })
}
render() {
return (
<div>
<Child changeText={this.handleCallback} />
{this.state.text}
</div>
)
}
}
Child Component
class Child extends React.Component {
render() {
return (
<div>
<button onClick={() => this.props.changeText("Data from child")}>
Button
</button>
</div>
)
}
}
The reason it is set up that way is because props are passed by reference. Should you choose to change the parent's state traditionally state = {"something"} instead of setState({"something"}), the object will be modified in the child component as well but there will not be a re-render cycle. The user won't see UI changes until a manual refresh.
And when you use setState to change the parent's state (standard practice), the re-render cycle will be triggered as expected and the Child Component will reflect the UI changes.
Note: Be warned, the use of functional components is almost defacto now. Makes life easy with integrations of Hooks and data stores such as Redux in my opinion.

React Best Practices: Having properties other than `state` in a component

I'm reviewing a React Component and it contains a state property as well as an allData property.
To follow best practice, shouldn't allData be a part of state?
constructor(props) {
super(props);
this.allData = [];
this.state = {
allDisplayedData: [],
allRowsCount: -1,
favData: [],
favRowsCount: -1,
};
this.searchAll = this.searchAll.bind(this);
this.handleCellClick = this.handleCellClick.bind(this);
}
If you want the component to re-render on changing it, then it needs to be in state, otherwise put it wherever. Any instance variables other than this.state aren't part of React's control, so they don't have the same ability to set using setState. This means that they don't re-render the component like the state does.
Essentially, it depends on what you do with it and how you want to work with it.
I tend to use this pattern for things like cancelTokens and intervalIds and other data I might need later, but don't need as part of the state because it's only needed in unmount or update but not in the render itself.
If it's needed in the render, you should have it in state or be prepared to deal with the component not rendering when it's updated.
If you want to make the array part of state, then yes, if no, then no.
Other than the two previous detailed answers, I found the following statement at https://reactjs.org/docs/state-and-lifecycle.html
While this.props is set up by React itself and this.state has a
special meaning, you are free to add additional fields to the class
manually if you need to store something that doesn’t participate in
the data flow (like a timer ID).

MapStateToProps to component state in an sync context

I know we can easily send the content of mapStateToProps in the component's state by doing so :
constructor(props){
super(props);
this.state = {
filteredApps: this.props.apps
}
}
In this usecase, this.state.filteredApps gets filled with what was mapped to props from Redux.
But what if this.props.apps is only filled properly after an async call? In an async context, this.props.apps will probably be an empty array for when it is initialized until the real data is fetched. Take this as an example :
class AppFilterer extends React.Component {
constructor(props) {
super(props);
this.state = {
filteredApps : this.props.apps
}
}
componentWillMount() {
this.props.getApps();
}
render(){ return <div> </div> }
}
const mapStateToProps = state => {
let { apps } = state.Admin;
return { apps };
};
export default connect(mapStateToProps, { getApps })(AppFilterer);
In this case, my Redux action (which is caught by an Saga) this.props.getApps(); is the call that fills my props full of apps and is called from the componentWillMount function. It is initialized as an empty array and then gets filled with apps once the call is complete.
I wish to filter these apps once they are fetched from the API so want to put them inside my component's state so that I don't mess with the Redux state. What is the best practice for updating the component's state in this case? In other words, is there any way to take the result of a saga that has been mapped to props and set it into the component's state or am I looking for a weird pattern and should filter it some other way?
First of all API calls go in componentDidMount not in componentWillMount which is also now deprecated. Please refer this guide:
https://reactjs.org/docs/react-component.html
Secondly, when you are using redux state and mapping it to props, you should not set that in your component local state, that’s not a good practice. You’ll receive updated props when your promise will return and you can always rely on props in that scenario.
But if you still want to do that you can override componentDidUpdate(prevProps) which will be called when your props or state is updated. Here is where you can set your state if you still want to do that.
Note for your filter thing
You can do filtering in componentDidUpdate method like:
this.setState({filteredApps. this.props.apps.filter(<your filter logic>)})

Does react setState execute if the old and new value are the same?

Initial state:
this.state = {
someVar: true
};
What if I have a code that set state value the same as the old one:
this.setState({
someVar: true
});
Does the second snippet execute? And does it cost the performance if it does execute and run many times?
Does the second snippet execute? And does it cost the performance if it does execute and run many times?
Yes and yes. However, there are two approaches to preventing this from happening.
Extending with PureComponent
Normally when writing a React Component you would write:
class MyComponent extends React.Component {
If you want React to perform a shallow comparison on the new and old props and state, you would write instead:
class MyComponent extends React.PureComponent {
This is the simpler approach. More info from the official documentation, here.
Manually checking on shouldComponentUpdate()
You can always perform this logic yourself and tell React when to ignore a re-render with the shouldComponentUpdate() lifecycle method.
shouldComponentUpdate(nextProps, nextState) {
if(this.state.someVar === nextState.someVar) return false;
return true;
}
In the above example, the component will not re-render if someVar is the same across renders. However, any other update to another state or prop variable will trigger a re-render.
More info from the official documentation, here.
Yes, it does if you don't have any comparison regarding state in your shouldComponentUpdate.
You can do this by default if your component extends PureComponent instead.
You can read more here.
Setting state will only set new values, there is no comparison with the previous state. shouldComponentUpdate is then invoked to decide whether render should take place or not.
Note that it returns true by default.
Actually you can call multiple setStates and React may batch multiple setState() calls into a single update for performance. But keep in mind that this will be async (setState updates doc).
As for setting the same state, all other answers are correct. SetState will execute even when new value is equal.

React: Can only update a mounted or mounting component

This is my component:
var booksRef = new Firebase("https://bookshelf.firebaseio.com/books");
class BookShelf extends React.Component {
constructor(props){
super(props);
this.state = {books: [] };
var self = this;
booksRef.on("value", function(snapshot){
const newbooks = [];
var firebaseBooks = snapshot.val();
for(var bookId in firebaseBooks){
newbooks.push({key: bookId, book: firebaseBooks[bookId]});
}
var newState = self.state;
newState.books = newbooks;
self.setState(newState);
});
}
...
When I navigate to this component for the first time, there is no problem. But when I navigate to another component and then back again to this component, I get the following warning in the console:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the component.
I guess I need to do something before I dispose the component, but I'm not sure why.
To your question, all the code you have in the class constructor above is done before mounting its essentially: componentWillMount, so that logic is ONLY being run at that point.
Now thats fine and all, but the issue is complicated buy the asynchronous request you have with firebase. There used to be a method called isMounted that you could just run a check on but now that is deprecated, the best practices for your scenario are outlined here: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
Check out this blog post about another (but hackey) ways to solve the issue: http://jaketrent.com/post/set-state-in-callbacks-in-react/
scroll to the es6 part, the first bit isn't directly relevant.
Additional:
Check out this babel blog post: Specifically the section on Classes:
Not sure if you need it, but its good
https://babeljs.io/blog/2015/06/07/react-on-es6-plus

Categories