React - using state value only one time - reactjs

I am a beginner on React, and I am confused on how to achieve this
I have a simple function which returns a component
renderCustomButtonText(style) {
return (
<span>
<input
value={this.state.contact}
placeholder="Custom Button Text"
/>
</span>
);
}
The input value is received from the state. When loading the page for the first time it gets this value from the database so that is okay, but when updating the state again, it gets changed again.
So, basically, I want to store {this.state.contact} somewhere so that I can get the initial value of it. And NOT getting the updated store value, only the initial value which was loaded the first time.
I don't want to use Redux for such a simple use case. Any ideas?

You can't use state with functional components you need class-based component, with functional components you are limited to render props.
When you need to interact with state, or component lifecycle hooks you need to use class-based components.

React updates its component whenever you use this.setState.
If you don't want to re-render your component, I would recommend setting the information to the this-instance instead of this.state.
e.g.
this.contact = contact;
However, since you're using the value prop of input, you need to update it each time the user inputs. What you could do is setting the state in the componentWillMount method with setState.
this.setState({ contact: '...' });
Then also update the state when the user inputs by adding a onInput event to the input component.
<input
value={this.state.contact}
onInput={e => this.setState({ contact: e.target.value })}
/>

Related

React: Best way to set state in parent with value from child

First question here i go... I´ve been searching and couldn´t find the best practice.
My solution (Maybe stupid):
I have a parent form that has a child select.
const [periodo, setPeriodo] = useState('0')
const cambiarPeriodo=(e)=> { setPeriodo(e) }
<NewSelect value ={periodo} onchange={(e) => { cambiarPeriodo(e) }} />
The child function component select:
const periodosChange=(e)=> {
props.onchange(e.target.value);
}
<Select value={props.value} custom name="periodo" id="periodo" onChange={periodosChange} >
Options are populated via axios database query with a useEffect.
When user selects an option in list, fires a onchange callback function and set state in parent.
Because of setState, the parent and child reloaded and cleared the user selection. The solution was to pass via prop the state to the child and assign it in value of select. <Select value={props.value}..
Is there any better way to do it, or this is a standard procedure? Actually i would like the child not to reload at all because the select list wont ever change... Tried React.memo but couldn´t make it work
There is a class-based component lifecycle's hook called shouldComponentUpdate() which you can use to prevent the child component from re-rendering if the list won't change. But this means you will have to convert your child component from a functional component to a class-based component.
The other option would be to use useMemo() and memoize the entire component as shown here.

using local state for input value with redux store

i started learning redux and i've got the following question. so I have a redux store with initial state and i also have a component which is responsible (dispatching an action) with user inputted data. so if the inputs are controlled should i set their value property in redux store (initalState) and dispatch an action every time value changes or should i use local state of component -
class Calc ...
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
inputVal: ''
}
}
handleChange(e){
this.setState({
[e.target.name]: e.target.value
})
}
render(){
return(
<div>
<input type="text" name="inputVal" value={this.state.inputVal} onChange={this.handleChange} />
</div>
)
}
in my opinion, before you store something in redux store, you should ask yourself some questions:
if my state "inputVal" will be shared between the other components?
if the change of my "inputVal" have some impacts on the other components
If you get the absolute answer "YES", you might need to store this state "inputVal" in redux state.
If NO, you do NOT need to store this state in redux. Instead of it, just handle the state within your component "Cal"
When a component is connected to Redux store it gets notified about the changes done to the store.
Therefore if
- two or more components would benefit from being notified about the state changes and
- these components are not in parent-child type of relationship when it's very easy for typically the parent to be aware about the changes related to self and the child and then let the child know what it should do by changing its props
then use Redux store.
Additionally there might be entirely different situation when just one single (or maybe several) component(s) need to be notified about navigation changes e.g user using Back/Foward browser buttons. In such case use Redux as well, in conjuction with Router that automatically dispathes an action to Redux store with relevant payload.

`componentWillReceiveProps` explanation

I recently wanted to upgrade my knowledge of React, so I started from the component lifecycle methods. The first thing that got me curious, is this componentWillReceiveProps. So, the docs are saying that it's fired when component is receiving new (not necessarily updated) props. Inside that method we can compare them and save into the state if needed.
My question is: Why do we need that method, if changes in props of that component (inside parent render) will trigger the re-render of this child component?
One common use case are state (this.state) updates that may be necessary in response to the updated props.
Since you should not try to update the component's state via this.setState() in the render function, this needs to happen in componentWillReceiveProps.
Additionally, if some prop is used as a parameter to some fetch function you should watch this prop in componentWillReceiveProps to re-fetch data using the new parameter.
Usually componentDidMount is used as a place where you trigger a method to fetch some data. But if your container, for example, UserData is not unmounted and you change userId prop, the container needs to fetch data of a user for corresponding userId.
class UserData extends Component {
componentDidMount() {
this.props.getUser(this.props.userId);
}
componentWillReceiveProps(nextProps) {
if (this.props.userId !== nextProps.userid) {
this.props.getUser(nextProps.userId);
}
}
render() {
if (this.props.loading) {
return <div>Loading...</div>
}
return <div>{this.user.firstName}</div>
}
}
It is not a full working example. Let's imagine that getUser dispatch Redux action and Redux assign to the component user, loading and getUser props.
It 'serves' as an opportunity to react to the incoming props to set the state of your application before render. If your call setState after render you will re-render infinitely and that's why you're not allowed to do that, so you can use componentWillReceiveProps instead.
But... you are beyond CORRECT in your confusion, so correct in fact that they are deprecating it and other Will-lifecycle hooks Discussion Deprecation.
There are other ways to accomplish what you want to do without most of those Will-lifecycle methods, one way being don't call setState after render, just use the new incoming props directly in render (or wherever) to create the stateful value you need and then just use the new value directly, you can then later set state to keep a reference for the next iteration ex: this.state.someState = someValue, this will accomplish everything and not re-render the component in an infinite loop.
Use this as an opportunity to react to a prop transition before render() is called by updating the state using this.setState(). The old props can be accessed via this.props. Calling this.setState() within this function will not trigger an additional render.
Look at this article
the componentWillReceiveProps will always receive as param "NxtProps", componentWillReceiveProps is called after render().
some people use this method use this to compare nxtProps and this.props to check, if something should happen before the component call render, and to some validations.
check the react's documentation to know more about react lifecycle!
hope this could help you!
changes in props of that component (inside parent render) will trigger the re-render of this child component
You are absolutely right. You only need to use this method if you need to react to those changes. For instance, you might have a piece of state in a child component that is calculated using multiple props.
Small Example:
class Test extends Component {
state = {
modified: "blank"
};
componentDidMount(){
this.setState({
modified: `${this.props.myProp} isModified`
});
}
componentWillReceiveProps(nextProps) {
this.setState({
modified: `${nextProps.myProp} isModified`
});
}
render() {
return <div className="displayed">{this.state.modified}</div>
}
}
In this example, componentDidMount sets the state using this.props. When this component receives new props, without componentWillReceiveProps, this.state.modified would never be updated again.
Of course, you could just do {this.props.myProp + "IsModified"} in the render method, but componentWillReceiveProps is useful when you need to update this.state on prop changes.

How does a redux connected component know when to re-render?

I'm probably missing something very obvious and would like to clear myself.
Here's my understanding.
In a naive react component, we have states & props. Updating state with setState re-renders the entire component. props are mostly read only and updating them doesn't make sense.
In a react component that subscribes to a redux store, via something like store.subscribe(render), it obviously re-renders for every time store is updated.
react-redux has a helper connect() that injects part of the state tree (that is of interest to the component) and actionCreators as props to the component, usually via something like
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
But with the understanding that a setState is essential for the TodoListComponent to react to redux state tree change(re-render), I can't find any state or setState related code in the TodoList component file. It reads something like this:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
Can someone point me in the right direction as to what I am missing?
P.S I'm following the todo list example bundled with the redux package.
The connect function generates a wrapper component that subscribes to the store. When an action is dispatched, the wrapper component's callback is notified. It then runs your mapState function, and shallow-compares the result object from this time vs the result object from last time (so if you were to rewrite a redux store field with its same value, it would not trigger a re-render). If the results are different, then it passes the results to your "real" component" as props.
Dan Abramov wrote a great simplified version of connect at (connect.js) that illustrates the basic idea, although it doesn't show any of the optimization work. I also have links to a number of articles on Redux performance that discuss some related ideas.
update
React-Redux v6.0.0 made some major internal changes to how connected components receive their data from the store.
As part of that, I wrote a post that explains how the connect API and its internals work, and how they've changed over time:
Idiomatic Redux: The History and Implementation of React-Redux
My answer is a little out of left field. It sheds light on a problem that led me to this post. In my case it seemed the app was Not re-rendering, even though it received new props.
React devs had an answer to this often asked question something to the tune that if the (store) was mutated, 99% of the time that's the reason react won't re-render.
Yet nothing about the other 1%. Mutation was not the case here.
TLDR;
componentWillReceiveProps is how the state can be kept synced with the new props.
Edge Case: Once state updates, then the app does re-render !
It turn out that if your app is using only state to display its elements, props can update, but state won't, so no re-render.
I had state that was dependent on props received from redux store. The data I needed wasn't in the store yet, so I fetched it from componentDidMount, as is proper. I got the props back, when my reducer updated store, because my component is connected via mapStateToProps. But the page didn't render, and state was still full of empty strings.
An example of this is say a user loaded an "edit post" page from a saved url. You have access to the postId from the url, but the info isn't in store yet, so you fetch it. The items on your page are controlled components - so all the data you're displaying is in state.
Using redux, the data was fetched, store was updated, and the component is connected, but the app didn't reflect the changes. On closer look, props were received, but app didn't update. state didn't update.
Well, props will update and propagate, but state won't.
You need to specifically tell state to update.
You can't do this in render(), and componentDidMount already finished it's cycles.
componentWillReceiveProps is where you update state properties that depend on a changed prop value.
Example Usage:
componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}
I must give a shout out to this article that enlightened me on the solution that dozens of other posts, blogs, and repos failed to mention. Anyone else who has had trouble finding an answer to this evidently obscure problem, Here it is:
ReactJs component lifecycle methods — A deep dive
componentWillReceiveProps is where you'll update state to keep in sync with props updates.
Once state updates, then fields depending on state do re-render !
This answer is a summary of Brian Vaughn's article entitled You Probably Don't Need Derived State (June 07, 2018).
Deriving state from props is an anti-pattern in all its forms. Including using the older componentWillReceiveProps and the newer getDerivedStateFromProps.
Instead of deriving state from props, consider the following solutions.
Two best practice recommendations
Recommendation 1. Fully controlled component
function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
Recommendation 2. Fully uncontrolled component with a key
// parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
Two alternatives if, for whatever reason, the recommendations don't work for your situation.
Alternative 1: Reset uncontrolled component with an ID prop
class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that's just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
Alternative 2: Reset uncontrolled component with an instance method
class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}
As I know only thing redux does, on change of store's state is calling componentWillRecieveProps if your component was dependent on mutated state and then you should force your component to update
it is like this
1-store State change-2-call(componentWillRecieveProps(()=>{3-component state change}))

React input defaultValue doesn't update with props changes

I have created an app using React, Redux & React Router. When the URL changes, the value of the query parameter personId should be used to get the correct person object from an array:
mapStateToProps = (state, router) => ({
onePerson: state.persons.filter((person, i) => person.id === router.params.personId)[0]
})
I also have an input element that depends on that person's name:
<input type="text" defaultValue={this.props.onePerson.name} />
As the URL changes, and the props updates accordingly, the input element's defaultValue doesn't change. How can I overcome this issue? Note that I don't want to use "controlled input".
That's because the defaultValue is only used the first time the component gets rendered. If you need to update the value later, you have to use a controlled input.
Update:
From the React Docs:
The defaultValue and defaultChecked props are only used during initial
render. If you need to update the value in a subsequent render, you
will need to use a controlled component.
I guess you could remove and re-add the component to give it a new defaultValue, but you really shouldn't.
I find easy to destroy an element, clean all data and replace it with the new one with :
ReactDOM.unmountComponentAtNode(document.querySelector("#myParentElement"));
if(document.querySelector("#myParentElement")){
ReactDOM.render(
<MyComponent name={name} />,
document.querySelector("#myParentElement")
);
};
You can use also this version of unmount method:
ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).parentNode);

Resources