React input defaultValue doesn't update with props changes - reactjs

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);

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.

Force update all the components in React#16.8.6

I want to update all the components in my React application (no matter what is the nesting level or if there is any prop or state change).
I tried using this.forceUpdate() but as some of the components don't use any props or state, those and their children are not updating.
Also, I tried the solution mentioned in the example but this also didn't work.
App.js
render() {
return (
<div>
<Routes />
</div>
);
}
As you can see, Routes don't take any props, so this component is not re-rendering. As a result of which, none of the components inside Routes is updating.
One way is using the store. As this is a small operation only, so I don't want to use the store for this kind of use case.
If you really need to update components on every render, you can use a key prop that's different every time the component renders. This will force React to unmount the previous instance of component and mount the new one with all the state and props being reset. In case of Route the easiest is to assign key as the current page url:
render() {
return (
<div>
<Routes key={window.location.href} />
</div>
);
}
With other components it's a bit more tricky, since you'll need to manually ensure the key is different each time.
If you would like to have more granular control over when the components are updated, you can hook the key prop to the state and change it onClick.
This post explains the approach in more detail.

React - using state value only one time

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 })}
/>

How can I make a ReactJS textarea load with initial content from props, but accept updates both from incoming props AND the user?

I have a React component with a <textarea> in it. I want this <textarea> to have all of the following characteristics:
editable, e.g. must use defaultValue OR (value WITH onChange)
initial content determined by incoming prop
content automatically changes when incoming props change
If I use defaultValue, then the <textarea> will not update when the component updates due to modified props from the parent.
If I use the value with onChange model as described in the React docs and in this SO answer, I fall into a trap and can't use incoming props from the parent to modify the state of the <textarea> because:
the <textarea>'s value must point to its component's state
component state can only be set by incoming props in getInitialState; setState can't be used in componentDidUpdate, for example; so I can never update state, and thus the value of my <textarea>, on a re-render
So my question is, how can I have a <textarea> in ReactJS whose value can be updated by the user, by incoming props on initial load, and also by updates to incoming props?
The only nasty workaround I've thought of so far is to use a "non-managed" state, my own JSON props tacked onto the component in place of formal React state. But this is not "the React way."
Simplified code follows in case it helps.
This code works on initial component load, but never uses props again so can't update the <textarea> content from props.
var Input = React.createClass({
componentDidUpdate: function() {
// when component updates, can't use setState here to update state,
// and thus can't update <textarea> content
},
getInitialState: function() {
return {
draftPath: this.props.draftPath,
draftBody: this.props.draftBody
};
},
handlePathChange: function(event) {
this.setState({
draftPath: event.target.value
});
},
handleBodyChange: function(event) {
this.setState({
draftBody: event.target.value
});
},
render: function() {
return (
<div id='cyoag-input-container'>
<textarea id='cyoag-input-path' type='text'
value={this.state.draftPath} onChange={this.handlePathChange} >
</textarea>
<textarea id='cyoag-input-body' type='text'
value={this.state.draftPath} onChange={this.handleBodyChange} >
</textarea>
</div>
);
}
}
You can use componentWillReceiveProps(nextProps) to update your component state with the incoming property.
Don't forget to compare your component props with the nextProps in order to call setState only if it's necessary.

React: Losing ref values

I am using two components and I am using this pattern: child component should stay isolated as much it can - it is handling its own validation error. Parent component should check for errors which have dependencies between children. So, in my case: password field and password confirmation field.
Here is my code:
a) SignUp (parent)
Setting initial state.
constructor() {
super();
this.state = {
isPasswordMatching: false
};
}
In render() method I am outputting my child component. Through prop called callback I am propagating method isPasswordMatching() by binding parent's this. The goal is that method can be called within child component.
<TextInput
id={'password'}
ref={(ref) => this.password = ref}
callback={this.isPasswordMatching.bind(this)}
// some other unimportant props
/>
<TextInput
id={'passwordConfirm'}
ref={(ref) => this.passwordConfirm = ref}
...
isPasswordMatching() method is checking if passwords match (through refs this.password and this.passwordConfirm) and then updates state. Please note that this method is called inside child (password or passwordConfirm).
isPasswordMatching() {
this.setState({
isPasswordMatching: this.password.state.value === this.passwordConfirm.state.value
});
}
b) TextInput (child)
Setting initial state.
constructor() {
super();
this.state = {
value: '',
isValid: false
};
}
On blur validation is done and state is updated.
onBlur(event) {
// doing validation and preparing error messages
this.setState({
value: value,
isValid: this.error === null
});
}
Latest. Callback prop is called.
componentDidUpdate(prevProps) {
if (prevProps.id === 'password' || prevProps.id === 'passwordConfirm') {
prevProps.callback();
}
}
Issue
Somehow my refs are lost. Scenario:
Parent component is renderder
Child components are rendered
I am entering one of input fields and get out (this invokes onBlur() method) - state gets updated, child component is rendered
componentDidUpdate() is invoked and prevProp.callback() as well
When going to isPasswordMatching() method I am outputting this.password and this.passwordConfirm - they are objects with expected values of reference. Updating state of parent - component gets rendered.
Then again all children are rendered, components get updated, callback is called but this time this.password and this.passwordConfirm are null.
I have no idea why references are kinda lost. Should I be doing something differently?
Thank you in advance.
See the react documentation here, with important warnings and advise about when to use or not to use refs.
Note that when the referenced component is unmounted and whenever the ref changes, the old ref will be called with null as an argument. This prevents memory leaks in the case that the instance is stored, as in the second example. Also note that when writing refs with inline function expressions as in the examples here, React sees a different function object each time so on every update, ref will be called with null immediately before it's called with the component instance.
I'm not sure if this answers #be-codified's question for not, but I found this running into a similar issue. In my case, it turned out that it was due to using a functional component.
https://reactjs.org/docs/refs-and-the-dom.html#refs-and-functional-components
Refs and Functional Components
You may not use the ref attribute on functional components because they don’t
You should convert the component to a class if you need a ref to it, just like you do when you need lifecycle methods or state.
You can, however, use the ref attribute inside a functional component as long as you refer to a DOM element or a class component
The documentation explains what you should do to solve the issue if you have control of the component you're trying to render.
However in my case, the component was from a 3rd party library. So, simply wrapping the component worked fine.
Working
<div ref={element => this.elementName = element}>
<FunctionalComponent />
</div>
Not Working
sets this.elementName to null
<FunctionalComponent ref={element => this.elementName = element} />
Hope this helps anyone finding this question like I did.

Resources