reactjs retain field props - javascript

how to retain component props after emitting back event.
Desc: i have a container it has multiple components (in a same view) in one of component has two fields and one button "continue". After entering some values into those fields , clicking on continue button its going to another component in this component we have two fields and two buttons one is back and continue when i click on back button its going to previous component but not retaining the props which entered on those . can you help me how to retain data.

The simplest solution for you is to have a parent component which holds all the data in its state. Parent container component passes the data to the children components you have as props, also it passes the callbacks to so a child can update the data. Given that you parent is always rendered and thus is never unmounted the data will be always there.
Something like this:
class Parent extends React.Component {
state = { name: 'John' }
render() {
return <div>
<Child name={this.state.name} onNameChange={e => this.setState({name: e.target.value})} />
</div>
}
}
class Child extends React.Component {
render() {
return <div>
<p>The name "{this.props.name}" is saved in the Parent</p>
<input type="text" value={this.props.name} onChange={this.props.onNameChange} />
</div>
}
}
A more complicated solution is to use something like Flux or Redux, but I believe it will be a bit too much for you now.

Related

Why is this react handleChange not working? Trying to handle a change received by props

I have an input field in react, whose value is initially set by a props received by the component
<EditUserModal
show={this.state.showModal}
close={this.closeModal}
userId={this.state.rowId}
email={this.state.selectedMail}
/>
As you can see it received the value email, and inside the component I can see the valu changing properly
<div class="form-group emailgroup">
<label for="exampleFormControlInput1">Change Email Address</label>
<input
type="email"
class="form-control"
placeholder="name#example.com"
value={this.props.email}
onChange={this.handleChange}
/>
</div>
Problem is, that I add a handleChange event, to be able to modify that input, but it doesnt modify the email, I suppose that its because I should change the props in the parent component, but I dont understand how to do it. Currently my handleChange function looks like this.
handleChange = (evt) => {
this.setState({
email: evt.target.value
});
}
And this is the initial state fo the component
this.state = {
email : ""
};
Obviously since im not calling the state, but the props to asign the value of the field, it doesnt change. But ive tried....
this.state = {
email : props.email
};
And it lets me change it this way, but the problem is that it will render nothing initially in the input value.
Don't assign your props to state, instead call your function from parent and pass state value to child component.
you can refer this for better understanding of parent to child data flow.
How to change the state of a child component from its parent in React
You are passing the wrong value into the component. Current you have this:
email={this.state.selectedEmail}
But when you look at what you're setting in the parent component, it's not 'selectedEmail' but 'email'. Changing the property should fix it:
email={this.state.email}
Then the correct value should be worked on.

ReactJS: Save input value on blur and not on every key stroke

I have created a React View, say MyView, which has 2 text inputs whose initial values will be passed by parent read from a DB.
I also want the changed values to be saved back to DB. So, the view is also passed a callback function for the same.
Consider that DB save operation is heavy and you should not do it very frequently. So, I decided to listen to onBlur events instead of onChange events on the input boxes as onChange is invoked on every key stroke.
First Approach:
class MyView extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<input type="url" value={this.props.values.A}
onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
<input type="url" value={this.props.values.B}
onBlur={(evt)=>{this.props.saveValue('B', evt.target.value)}} />
<button type="button" onClick={this.props.resetValues}>Reset</button>
</div>
);
}
}
However, this does not work as React enforces a controlled input (with value attribute) always to be accompanied by an onChange listener.
Second Approach:
So, I tried to make these inputs as uncontrolled. That is, instead of value attribute, used defaultValue.
<input type="url" defaultValue={this.props.values.A}
onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
But this also did not work as on reset/clear button click, although the view was made to re-render but defaultValue does not update once view is created.
Third Approach:
So, I finally added an onChange listener but as no-op.
<input type="url" value={this.props.values.A}
onChange={()=>{console.log('do nothing')}
onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
Again, this did not work as the view re-renders after calling onChange and since value is not reflected in props yet, value seems to reset back to initial on every key stroke.
Fourth Approach:
Last I tried was to maintain a state in component and read value from state and on every onChange save the value back to state. This worked to most extent but whenever there were external changes to props and the view was re-rendered, state did not update. So, I added a getDerivedStateFromProps function to view:
static getDerivedStateFromProps(props, state) {
return props.values;
}
Now, this again did not work. Reason being that this function is invoked even if I temporarily save values to state and the state was reset to initial values in props.
Can some ReactJS expert help me with my use-case?
You will still need onChange to help you set the states of both url input. onBlur is only used to trigger saving, it's 2 different events for different purposes.
Since your A & B values are passed down from parent component. MyView's parent component should pass down this.state.values and the functions to set the state.
Refer to this snippet if everything is in single component. You should be able move handleChange function up to its parent component.
class App extends React.Component {
state = {
values: {
A: '',
B: ''
}
}
handleChange = e => {
this.setState({
values: {
...this.state.values,
[e.target.name]: e.target.value
})
}
handleBlur = e => {
if (e.target.name === 'A') {
alert(`Saving A: ${this.state.values.A}`)
}
if (e.target.name === 'B') {
alert(`Saving B: ${this.state.values.B}`)
}
}
render() {
return (
<div>
<label>Value A</label>
<input
type="url"
name="A"
value={this.state.values.B}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
<label>Value B</label>
<input
type="url"
name="B"
value={this.state.values.A}
onChange={this.handleChange}
onBlur={this.handleBlur}
/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
</div>
EDIT: Your fourth approach should work with the following:
static getDerivedStateFromProps(props, state) {
return { values: props.values }
}
constructor(props) {
super(props)
this.state = {
values: props.values
}
}
so basically the this.state.values is the final source of truth. When user types something, you setState in this component and change it. But if props.values changes (from external source), getDerivedStateFromProps will update the values state.
Going by the comments on Liren Yeo's solution, I would handle the props-state reconciliation on componentDidUpdate, where you get both the old state and props. This way you can determine how this.props was updated and act accordingly. When the value in props does not match state nor oldProps, the update is external and you should override the unsaved changes in the state.
The code should look something like this
componentDidUpdate(prevProps) {
if (this.props.values !== prevProps.values && this.props.values !== this.state.values) {
this.setState({values:this.props.values});
}
}
If you go this route, you can also leave the input uncontrolled and update its value through a reference. This solves some unreliability with controlled inputs, like for example, a type='number' returning undefined as its value when you type a decimal comma. You still need to store the value onChange but only save it onBlur and handling the state-prop-dom reconciliation in componentDidUpdate
So with the idea that onChange works, I would recommend you to take a look at this:
https://schier.co/blog/2014/12/08/wait-for-user-to-stop-typing-using-javascript.html
Navigate to the heading: Wait for Typing to Stop
Hope it can somehow lead you to what you want to achieve.

Is there a way to render only after only a complete input? Delaying render() - ReactJS

I have some Controlled Component text inputs for a form:
<input type="text" onChange={(e) => this.props.changeBusiness(e)}/>
I also want to render the (above) text input in a separate component - however! It would be nice if I could render it with the COMPLETED text input to give (in my opinion), what would be a better feel.
How might this be implemented? I assume I could have an onBlur
handler that is triggered.
Is there any way to delay the render() till only after the complete text input?
Danke
Yes if you want to delay a render you just need to return null in the render based on a flag. Lets say you have a state variable inputBlurred that you pass around as a prop to something else.
render() {
if (!this.props.inputBlurred) return null;
... more here
}
You would use it like so.
class ParentComponent extends Component {
constructor() {
super();
this.state = {inputBlurred: false}
}
handleBlur = (e) => {
if(e.target.value){
this.setState({inputBlurred: true});
}
}
render() {
return(
<div>
<input onBlur={this.handleBlur} />
<ChildComponent inputBlurred={this.state.inputBlurred} />
</div>
)
}
}
You have several posibilities.
Create custom Input wrapper around input component, which does trigger "change" event on "complete" change - e.g. on blur. While you typing, parent component won't receive incremental onChange events.
From parent, pass onBlur handler to child input component and as soon, as onBlur handler is triggered, render input's value wherever you wish.

binding multiple react components from the parent

I just got started in React and am confused about something. Let's say I have a Login controller view (smart component), and then a LoginForm view (dumb component), and within the LoginForm, I created custom components for to handle all my inputs on this page the same.
class TextInput extends React.Component {
render() {
return (
<input type="text"
name={this.props.name}
className="form-control"
placeholder={this.props.placeholder}
ref={this.props.name}
onChange={this.props.onChange}
value={this.props.value} />
);
}
}
class LoginForm extends React.Component {
render() {
return (
<TextInput
name="username"
placeholder="Username"
onChange={this.props.onChange}
value={this.props.username} />
<TextInput
name="address"
placeholder="Address"
onChange={this.props.onChange}
value={this.props.address} />
);
}
}
I'm then not sure how in my top level Login controller view's render function, when I create the
How do you get a different to be called for the different inputs? Because my top level component sets the properties down right? Since the bottom most component expects an onChange property, from the top level when I pass down onChange, aren't all TextInputs going to call the same onChange method instead of two separate methods?
Yes, the same function is called. What changes is the parameter. This is just old school JS. The input will pass the change event to to the onChange function as the first param.
You will need to handle that in one component or another.
onChange(e){
var formKeyVal = {};
formKeyVal[e.target.name] = e.target.value;
this.setState(formKeyVal);
}
Thanks to refs We can set up different referring to each low-level element. No matter, dumb or smart.
Your high-level component contains:
`handleText(){
this.setState({
text: ReactDOM.findDOMNode(this.refs.yourElement).value
})
}`
Your low-level component contains:
<input
type="text"
ref="yourElement"
onChange={this.props.textInput}
/>
This example gives you some clue. Pay attention to details: Selector: refs
but element: ref.
From time to time you need two level refs. In this case it would be
text: ReactDOM.findDOMNode(this.refs.yourComponent.refs.yourElement).value

Listen to global mouse event from React component

I would like to have a component that sets the content of it's <div> based on a data attribute of a different component the mouse is hovering over. For example, I would like the data-message attribute of this component:
<ComponentA data-message="This is a message" />
To set the state of ComponentB when the mouse enters ComponentA:
class ComponentB extends React.Component {
...
render() {
return (
<div>this.state.message</div>
);
}
}
There will possibly be 1000's of components with this data attribute.
How can I have a single mouse event listener which listens to any component with a certain data attribute?

Categories