Reactjs. Use Ref inside the component and outside - reactjs

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

Related

Only able to type one character at a time in React.Js Input when rendering from a function

code
I have to click the input after every character I type, because it loose focus. How can I do it, using the same function with name "InputArea".
Because this problem doesn't arises when I don't use the function and write the code directly inside return.
Define the InputArea component outside the parent component pass props to it. Because in your case, when the state changes, the component is re-rendered, and hence the InputArea component is redeclared.
Check out this article which addresses the same issue you have
function InputArea (props) {
return <input
type="text"
onChange={props.onChange}
value={props.value}
/>
}
function ParentComponent = () => {
const [name, setName] = useState('')
function onChange (e){
setName(e.target.value)
}
return (
<div>
<InputArea onChange={onChange} value={name} />
</div>
)
}
each time you call the setName function you rerender the InputArea function.
for fixing it you can wrap the function with a useCallBack hook or move the function out from the component.
const InputArea = useCallback(() => {
return (
<Input .../>
)
}, []);

Component Re-rendering issue

I am working on the project in React Typescript.
I have created hierarchy of components as per requirement.
In one scenario I have to pass data from child component to parent component and I am passing function as props and it works.
Issue :
When passing data to parent component child component gets re-render it looks like. Mean to say Dropdown selection is get reset and tree control expanded nodes get collapsed and set to the position as first time rendered.
I have used useState,useEffects hooks.
I have also tried React.memo as a part of my search on internet.
What I need :
I want to pass data to parent component from child without re-render the child component as there is no change in the props of child component.
Try this approach:
Add useCallback hook to memoize your function which lift data to <Parent />.
Then use React.memo for <Child /> to control prop changes and avoid unwanted re-renders.
I prepare an example for you here.
UPD. I have uploaded an example, you can copy it and see how it works!
Here is Child component:
const Child = ({ onChange }) => {
console.log("Child re-render");
return (
<div className="App">
<h1>Child</h1>
<button onClick={() => onChange(Math.random())}>
Lift value to Parant
</button>
</div>
);
};
const areEqual = ({ onChange: prevOnChange }, { onChange }) => {
return prevOnChange === onChange; // if true => this will avoid render
}
export default React.memo(Child, areEqual);
And the Parent:
consn App = () => {
const [value, setValue] = useState("");
const onChange = useCallback((value) => setValue(String(value)), []);
console.log("Parant re-render");
return (
<div className="App">
<h1>Parent</h1>
<div>Value is: {value}</div>
<Child onChange={onChange} />
</div>
);
}
Best regards šŸš€

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

Problem with re render in parent component

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

Resources