Problem with re render in parent component - reactjs

Firstchild file:
const [qtd, setQtd] = useState(props.number);
function updateNumber(){
props.setNumber(qtd);
}
const handleChange = event => {
setQtd(event.target.value);
};
return(
<input type="number" name="integerTimes" className="integerTimes" placeholder='Insira aqui' id="qtd" onChange={handleChange}/>
<button id="submitTimes" onClick={updateNumber}>Enviar</button>
);
Parent Component:
const [number, setNumber] = useState(0);
if (number>0){
return (
<div id='main'>
<div>
<Firstchild setNumber={setNumber} number={number}/>
<SecondChild number={number}/>
</div>
</div>
);
}
the problem is, i need 'number' to pass to other child component, but apparently when the parent re-render i lose the update that i made in the first child component. How can i mantain the value of 'number' after changing in the child component?

You should have single source of truth especially when you are working with inputs because and inputs have there local state and you have to reference it to an external source in order to work properly.
reference your input's value to your parents state.
return(
<input type="number" value={props.number} />
);

Related

Reactjs. Use Ref inside the component and outside

I wrote a custom input for passwords. He looks like this:
const InputPassword = ({placeholder}) => {
const inputRef = useRef()
const [isPasswordVisible, setPasswordVisible] = useState(false)
function setInputStatus() {
if (isPasswordVisible) {
inputRef.current.setAttribute('type', 'password')
} else {
inputRef.current.setAttribute('type', 'text')
}
setPasswordVisible(!isPasswordVisible)
}
return (
<div className={cl.main}>
<input
ref={inputRef}
type={"password"}
placeholder={placeholder}
className={cl.input}
/>
<div className={cl.eyeContainer} onClick={setInputStatus}>
<Eye
isPasswordVisible={isPasswordVisible}
/>
</div>
</div>
);
};
export default InputPassword;
Now I want to get the value from my input in the parent component. The best way to do this is to use a forwardRef, but my component already has an internal Ref that is needed to change the type of the input. Surely you can somehow use one Ref to solve these two problems, but I did not find how.
I tried to pass a Boolean type state from the parent, so that when this state changes, call a method in the child component, this method would change another state in the parent, which stores the value from the child. And when this parent state changes, the necessary logic would work out for me. But the code turned out to be just as cumbersome and confusing as what is written above. Naturally, such code does not work well, it shows unpredictable behavior. I'm sure the solution is much simpler.
You can also pass a state variable to the child component, I am passing a ref variable, assuming that the parent component does not need to re-render based on the changes in the value of variable that is being passed as a prop.
Your parent component should have a inputRef, and pass it as a prop to child component, something like this:
const Parent = () => {
const inputRef = useRef()
return <InputPassword inputRef={inputRef} />
}
export default Parent;
const InputPassword = ({placeholder, inputRef}) => {
const [isPasswordVisible, setPasswordVisible] = useState(false)
function setInputStatus() {
if (isPasswordVisible) {
inputRef.current.setAttribute('type', 'password')
} else {
inputRef.current.setAttribute('type', 'text')
}
setPasswordVisible(!isPasswordVisible)
}
return (
<div className={cl.main}>
<input
ref={inputRef}
type={"password"}
placeholder={placeholder}
className={cl.input}
/>
<div className={cl.eyeContainer} onClick={setInputStatus}>
<Eye
isPasswordVisible={isPasswordVisible}
/>
</div>
</div>
);
};
export default InputPassword

how do I change a input children value component?

I’m wondering how can I change a children input value from main?
Basically I have a component that I’m sending a children, and in this component I want to be able to change the input value.
Here is my main:
<FooterOptions>
<input type="text"
onChange={event=>setState(event.target.value)}
value={state}
hidden/>
</FooterOptions>
And here is my component:
export function FooterOptions(props:{children: ReactNode}){
return(
<div>
{props.children}
</div>
)
}
The children prop is something that you can only render onto the page, so there's nothing you can do with it to change the value of the input.
Instead how I would think about this is that you want to provide a mechanism for FooterOptions to change the value of another component. Here it happens to also be rendered as its children, but it would work the same even if it was rendered someplace else.
const Parent = () => {
const updateInput = (val) => setState(val)
return (
<FooterOptions handleUpdate={updateInput}>
<input type="text"
onChange={event=>setState(event.target.value)}
value={state}
hidden/>
</FooterOptions>
)
}
export function FooterOptions(props:{children: ReactNode, handleUpdate}){
// Example handler
const handleClick = () => handleUpdate('updated inside FooterOptions')
return(
<div onClick={handleClick}>
{props.children}
</div>
)
}
If you'd like to add more details of how you are hoping to update, then I can add a better example 😀

Multiple instances of same react component passing values to parent component

We have a page that has a tab on the top. Both tabs show same components, except that on one tab some of the labels and other controls are different (some are hidden, some appear only on one, etc.) The changes are minimal enough that they don't warrant a different component altogether.
So what I am trying to figure out is how to embed multiple instances of the same child component into a parent, and yet be able to pass values from different instances of the child to the parent.
Here is my parent component for example
import React, { useState } from "react";
import Child1 from "./Child1";
const Parent1 = () => {
const changeNameFunc = (childName) => {
alert(childName);
};
return (
<div>
Parent
<div>
<Child1 id="1" changeName={changeNameFunc} />
<Child1 id="2" changeName={changeNameFunc} />
</div>
</div>
);
};
export default Parent1;
And here is my child component
import React, { useState } from "react";
const Child1 = (props) => {
const [name, setname] = useState();
const clicketyclick = () => {
const newName = document.getElementById("txtName").value;
setname(newName);
};
return (
<div>
<input type="text" id="txtName" name="name" />
<button onClick={clicketyclick}>Change Child Name</button>
<button onClick={() => props.changeName(name)}>Send To Parent</button>
</div>
);
};
export default Child1;
In this case the problem is obviously in my clicketyclick function because the document.getElementById picks up the first control that matches that ID in the DOM. In my code at work, we aren't picking up values using document.getElement, rather we are passing state object up to the parent via a props.function call.
So to make it random, I changed my child code in this example to following.
import React, { useState } from "react";
const Child1 = (props) => {
const [name, setname] = useState();
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
const clicketyclick = () => {
const newName = getRandomInt(100); // document.getElementById("txtName").value;
setname(newName);
};
return (
<div>
<input type="text" id="txtName" name="name" />
<button onClick={clicketyclick}>Change Child Name</button>
<button onClick={() => props.changeName(name)}>Send To Parent</button>
</div>
);
};
export default Child1;
Now this works just fine. If I click ChangeChildName on first instance of Child control, it sets a random number (say 54) to the state of that instance. If I then click SendToParent on first instance of Child control, it alerts 54. If I click ChangeChildName on second instance of Child control, it sets a different random number (say 98) to the state of that instance. If I then click SendToParent on second instance of Child control, it alerts 98. This is great. It means the state is owned by the instance of the child control.
So with this knowledge, are there any gotchas with nesting multiple instances of a child component into a parent? Is it better to just avoid it and create a separate component even if 98% of the functionality is same? I've read somewhere that in this case we should make sure different instances have different "id" property (though taking it out didn't make any difference; my code still worked). I've also read somewhere about passing in a context of some sort to prefix the field ids with (not sure how that would work).
What is the best way to do this i.e. have a parent render multiple instances of a child control, and then have each child send their respective data up to the parent without causing any collisions or other gnarly problems with state management.
Edit: My question really isn't about the getElementById part. My question is what is the proper way to have multiple instances of a control embedded inside a parent component. Are you supposed to pass some sort of prefix for the ids in the child control, and if so how? Are you supposed to have an "id" attribute on the child control definition inside the parent control to make react aware of it that they are unique? Or something else?
You can use useRef
import React, { useState, useRef } from 'react';
const Child1 = (props) => {
const [name, setname] = useState('');
const inputRef = useRef(null);
const clicketyclick = () => {
const newName = inputRef.current.value;
setname(newName);
};
return (
<div>
<input type="text" name="name" ref={inputRef} />
<button onClick={clicketyclick}>Change Child Name</button>
<button onClick={() => props.changeName(name)}>
Send To Parent
</button>
</div>
);
};

Multiple value / onChange values in a React form

I must be missing something very simple, but I can't figure out how to deal with having 2 value / onChange to pass to my component.
I tried changing the names, and that gets rid of errors and renders the app, but of course event.target.value does not work as if I change the second value to, for example to numval or something. event.target.numval doesn't recognize anything is happening.
Thank you so much in advance! And if this has been asked before I apologize, but I couldn't find it... which makes me think I'm overlooking a very simple solution.
return (
...
<PersonForm
onSubmit={addName}
value={newName}
onChange={handleName}
value={newNumber}
onChange={handleNumber}
/>
)
Here is the original code that worked fine before I tried to put the component into its own file:
return (
...
<form onSubmit={addName}>
<div>
name: <input value={newName} onChange={handleName} />
</div>
<div>
number: <input value={newNumber} onChange={handleNumber} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
your code sample is not clear but it seems you want to retrieve value from event.target object through event handler function triggered by onChange event on
an element. And you want to call event handler function available in a parent component from a child component.
You can try the following:
const App () => {
const [newNameValue, setName] = useState('');
const [newNumberValue, setNumber] = useState('');
const nameChangeHandler = (event) => {
setName(event.target.value);
};
const numberChangeHandler = (event) => {
setNumber(event.target.value);
};
const submitFormHandler = () => {
your code on form submit
};
return (
<PersonForm
submitForm={submitFormHandler}
newName={newNameValue}
handleName={nameChangeHandler}
newNumber={newNumberValue}
handleNumber={numberChangeHandler}
/>
);
}
const PersonForm (props) => {
return (
<form onSubmit={props.submitForm}>
<div>
name: <input value={props.newName} onChange={props.handleName} />
</div>
<div>
number: <input value={props.newNumber} onChange={props.handleNumber} />
</div>
<div>
<button type="submit">add</button>
</div>
</form>
);
}
onChange is a reserved attribute name which is available on few html elements
such as <input> and <select>. You should ideally not use it on other elements, like in your case custom html component <PersonForm>.
Since you're keeping form in separate component then you can trigger a function call received through attributes on props object.
You may call two different onChange event handler functions on two input elements.
I hope you got your answer, feel free to ask in case of any more doubts.
You may refer:
https://reactjs.org/docs/forms.html

Value not updating inside a ReactJS function

I have a ReactJs function that displays a simple dialogue box, and is intended to update a value for the parent component. The function looks like this:
export function MyDialogue(props: IMyProps) {
var myValue = 0;
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" value={myValue} />
<button onClick={() => props.updateFunc(myValue)}>Update</button>
</div>
);
}
I've tried several variations of this; for example, passing in props.myValue, and even changing the function to a class and passing this.state.myValue in. In all but this example, myValue remains as it was in the parent class, and in this version, always 0.
updateFunc simply calls setState on the parent class, and having traced through it, it never gets called with the changed value.
I've seen some documentation that says to essentially handle the onChange event - is this the only way to make this work, or is there a way to implement data binding in React?
Just bind back your input to the parent's state via props.value;
MyDialogue.js
export function MyDialogue(props: IMyProps) {
return (
<div style={style}>
...
<input type="text" value={props.value} />
...
</div>
);
}
Parent.js
....
render(){
const { dialogueValue } = this.state;
return <MyDialuge value={dialogueValue} updateFunc={this.handleUpdate} />
}
You are using uncontrolled input because You are not keeping value of input inside state.
Solution is
With uncontrolled:
export class MyDialogue extends React.Component<IMyProps, {}>{
constructor() {
super();
this.input = React.createRef();
}
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" ref={this.input} />
<button onClick={() => props.updateFunc(this.input.current.value)}>Update</button>
</div>
);
}
With controlled:
Maintain myValue state in parent and pass it to child.
and on change on input event call a function of parent which change myValue using setState,
Maintain myValye state inside MyDialogue and onClick pass it to parent.
You need to change this component to a stateful component
then do a two-way binding for your textbox and have it talk to local state and then use that state value to update parent component.
export class MyDialogue extends React.Component<IMyProps, {}>{
constructor() {
super();
this.state = {
myValue: 0
}
}
onChangeHandler = (event:any) =>{
this.setState({myValue:event.target.value});
}
return (
<div style={style}>
<label>Enter a number here: </label>
<input type="text" value={this.state.myValue} onChange={this.onChangeHandler}/>
<button onClick={() => props.updateFunc(this.state.myValue)}>Update</button>
</div>
);
}

Resources