How do you pass / use refs inside a handler function inside a functional component?
import React, { useRef } from 'react';
function RegisterUser() {
const emailInput = useRef(null);
const passwordInput = useRef(null);
const handleClickRegister = () => {
RegisterPersonMutation(email, password, callbackValue => {
emailInput.current.value = ''; // <---------------------this guy
passwordInput.current.value = ''; // <------------------and his friend
});
};
return (
<div className="register-wrap">
<form>
<input type="text" ref={emailInput} />
<input type="password" ref={passwordInput} />
</form>
<button onClick={() => handleClickRegister()}>Register</button>
</div>
);
}
export default RegisterUser;
I would still like to know how to create and use refs inside nested functions using useRef... but in this case, Frank was right in that I should just use useState.
I'm already using it in my real component so it's like this:
function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
handleClick(foo, bar, callback => {
setEmail('');
setPassword('');
});
<input value={email} />
<input value={password} />
}
Related
I have a text input inside the tab view in which I have used setState for updating the value of the input.
export function Login(){
const [email, setEmail] = useState();
return(
<View>
<TextInput
onChangeText={setEmail}
value={email}
/>
</View>
)
}
Now every time I called setEmail on onChangeText input gets hides. Is there anything using which I can prevent the keyboard from getting close?
You need to use the synthetic event listener and set the state to the desired value:
import React from "react";
export default function App() {
const [email, setEmail] = React.useState('');
return (
<>
<input onChange={(e) => setEmail(e.target.value)} value={email} />
</>
);
}
This happens when you define a nested component as in the code below:
const ListMenu = () => {
const [searchText, setSearchText] = useState("");
const Header = () => {
return <TextInput
placeholder="Search"
onChangeText={text => { setSearchText(text) }}
/>
}
return (<View>
<Header />
</View>
)}
instead make nested component like this:
const ListMenu = () => {
const [searchText, setSearchText] = useState("");
const header = () => {
return <TextInput
placeholder="Search"
onChangeText={text => { setSearchText(text) }} />
}
return (<View> {header()} </View>)
}
Here I have to call useRef and have the focus method to be called on Ref object. Below is the Input.tsx component.
import React, { useRef, useState } from "react";
const Input = (props: any) => {
const [text, setText] = useState<string>('');
const inputRef = useRef<any>();
const onChangeHandler = (e) => {
setText(e.target.value)
};
const submitHandler = (event: React.FormEvent) => {
event.preventDefault();
if (!text) {
inputRef.current.focus();
}
};
return (
<div>
<form onSubmit={submitHandler}>
<label htmlFor="label">label</label>
<input
ref={inputRef}
value={text}
type="text"
id="email"
onChange={onChangeHandler}
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default Input;
If I don't define the useRef type to be 'any' I'm getting compile error. Where as defining it 'any' I'm getting runtime error of cannot read properties of undefined (reading 'focus'). I think I'm not initializing any value on useRef that's why I'm getting this error. But I also know I can't assign string, number or boolean value and call focus() there. How can I fix this issue. I'm using typescript by the way.
I found the answer to my own question. The type of useRef.current for a text input field usually would be <HTMLInputElement | null> it cannot be undefined. Therefore this is how the code should be.
import React, { useRef, useState } from "react";
const Input = (props: any) => {
const [text, setText] = useState<string>('');
const inputRef = useRef<HTMLInputElement>(null); //made a change here
const onChangeHandler = (e) => {
setText(e.target.value)
};
const submitHandler = (event: React.FormEvent) => {
event.preventDefault();
if (!text) {
inputRef.current!.focus(); //made a change here
}
};
return (
<div>
<form onSubmit={submitHandler}>
<label htmlFor="label">label</label>
<input
ref={inputRef}
value={text}
type="text"
id="email"
onChange={onChangeHandler}
/>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default Input;
Thanks to one of the comments below my question.
i found this quite interesting pattern where you enable function based on ternary approach applied on the use state itself but i got this problem where it says
React Hook "useState" is called conditionally. React Hooks must be
called in the exact same order in every component render
import React, { useState } from "react";
const App = () => {
const [enableFirstName, setEnableFirstName] = useState(false);
const [name, setName] = enableFirstName ? useState("") : ["", () => {}]; #Error is here
const [lastName, setLastName] = useState("");
const handleChangeName = (e) => {
setName(e.target.value);
};
const handleChangeLastName = (e) => {
setLastName(e.target.value);
};
const handleEnableChange = (evt) => {
setEnableFirstName(!enableFirstName);
};
return (
<div>
<h1>My name is: {enableFirstName ? name : ''} {lastName}</h1>
<input type="checkbox" value={enableFirstName} onChange={handleEnableChange} />
<input type="text" value={name} onChange={handleChangeName} />
<input type="text" value={lastName} onChange={handleChangeLastName} />
</div>
);
};
export default App;
React doesn't allow you to call hooks conditionally.
Remove the condition in the second useState:
const [enableFirstName, setEnableFirstName] = useState(false);
const [name, setName] = useState("");
const [lastName, setLastName] = useState("");
Even if you don't need the name because enableFirstName is false - simply ignore it, leave empty string and handle conditional logic in the component methods if necessary.
Link to original question: How to setState of computed property using hooks
I'm basically trying to update an old project to use hooks and functional components where possible. I need to update the state of a computed property using hooks in the handleChange function. How would I do this?
I've removed the irrelevant code in the following snippet:
import React, { useState } from 'react'
import AuthenticationService from '../service/AuthenticationService';
export const LoginComponent1 = () => {
const [userName, setUserName] = useState('Neil');
const [password, setPassword] = useState('');
const [loginStatus, setLoginStatus] = useState(false);
const handleChange = (event) => {
this.setState(
{
[event.target.name]: event.target.value
}
);
}
const loginClicked = () => {
...
}
const enterPressed = (event) => {
...
}
return (
<div className="container">
<h1>Login</h1>
<div className="container">
User Name: <input className= "userNameBox" type="text" placeholder="Neil" name="username" value={userName} onChange={handleChange} onKeyPress={enterPressed}></input>
Password: <input className= "userNameBox" type="password" id="myInput" name="password" value={password} onChange={handleChange} onKeyPress={enterPressed}></input>
<hr></hr>
</div>
</div>
)
}
export default LoginComponent1
Thanks!
While declaring a state in functional component the second parameter you are passing is a method used to set the state. this.setState is available only in class components. See here
In your example for setting the state password, you need to call the method setPassword with value.
Eg: setPassword('this is my password')
this.setState exists only in class components, not functional ones.
For updating state with useState hooks, simply call the corresponding setter. In your example, call setUserName to update the username, setPassword to update the password and so on.
if you want to create a generic handler like before you had at react based component with multiple state handlers you could create an object handler after your useState hooks and refactor your handleChange like:
const handler = {
username: setUserName,
password: setPassword
}
const handleChange = (event) => handler[event.target.name](event.target.value)
I believe it is because it binds the value back to the property inputText but just want to make sure I'm stating this correctly.
import React, { useState } from "react";
const InputElement = () => {
const [inputText, setInputText] = useState("");
return (
<div>
<input
placeholder="Enter Some Text"
onChange={e => {
setInputText(e.target.value);
}}
/>
</div>
);
};
export default InputElement;
This is a good example of 2 way data binding because when you update the state, the UI changes, and when the UI changes, the state changes. Just need to remind you to set the value prop on the <input> element to inputText so that it's a controlled component.
import React, { useState } from "react";
const InputElement = () => {
const [inputText, setInputText] = useState("");
return (
<div>
<input
placeholder="Enter Some Text"
onChange={e => {
setInputText(e.target.value);
}}
value={inputText}
/>
</div>
);
};
export default InputElement;