反应 useRef useImperativeHandle
React useRef useImperativeHandle
在下面的登录表单中,我在提交功能中使用了 document.getElementById()
。它有效,但我今天听说这不是一个很好的实践,我最好改用 useRef()
。
但经过一些研究,useRef()
无法使用自定义组件,例如我的 <FormInput/>
。经过一些重新研究,我听说了 forwardRef()
和 useImperativeHandle()
它仍然有效,但我很困惑,我的代码似乎更复杂且更难管理。
您对此用例有何看法?
提前致谢
初始代码
function Login() {
const navigate = useNavigate()
const dispatch = useDispatch()
async function handleSubmit(e) {
e.preventDefault()
await dispatch(
fetchOrUpdateLogin({
email: document.getElementById('username').value,
password: document.getElementById('password').value,
})
)
navigate('/user')
}
return (
<main className="main bg-dark">
<section className="sign-in-content">
<i className="fa fa-user-circle sign-in-icon"></i>
<h1>Sign In</h1>
<form onSubmit={handleSubmit}>
<FormInput
content="Username"
type="text"
idFor="username"
prefill={USER.email}
/>
<FormInput
content="Password"
type="password"
idFor="password"
prefill={USER.password}
/>
<div className="input-remember">
<input type="checkbox" id="remember-me" />
<label htmlFor="remember-me">Remember me</label>
</div>
<Button type="submit" content="Sign In" classStyle="sign-in-button" />
</form>
</section>
</main>
)
}
function FormInput({ content, type, idFor, prefill }) {
return (
<div className="input-wrapper">
<label htmlFor={idFor}>{content}</label>
<input type={type} id={idFor} autoComplete="off" defaultValue={prefill} />
</div>
)
}
最终代码
function Login() {
const navigate = useNavigate()
const dispatch = useDispatch()
const emailValue = useRef(null)
const passwordValue = useRef(null)
async function handleSubmit(e) {
e.preventDefault()
await dispatch(
fetchOrUpdateLogin({
email: emailValue.current.inputValue(),
password: passwordValue.current.inputValue(),
})
)
navigate('/user')
}
return (
<main className="main bg-dark">
<section className="sign-in-content">
<i className="fa fa-user-circle sign-in-icon"></i>
<h1>Sign In</h1>
<form onSubmit={handleSubmit}>
<FormInput
content="Username"
type="text"
idFor="username"
prefill={USER.email}
ref={emailValue}
/>
<FormInput
content="Password"
type="password"
idFor="password"
prefill={USER.password}
ref={passwordValue}
/>
<div className="input-remember">
<input type="checkbox" id="remember-me" />
<label htmlFor="remember-me">Remember me</label>
</div>
<Button type="submit" content="Sign In" classStyle="sign-in-button" />
</form>
</section>
</main>
)
}
const FormInput = ({ content, type, idFor, prefill }, ref) => {
const valueRef = useRef(null)
useImperativeHandle(ref, () => ({
inputValue() {
return valueRef.current.value
},
}))
return (
<div className="input-wrapper">
<label htmlFor={idFor}>{content}</label>
<input
ref={valueRef}
type={type}
id={idFor}
autoComplete="off"
defaultValue={prefill}
/>
</div>
)
}
export default forwardRef(FormInput)
我就是想问这个。为什么不尝试基于状态的方法而不是使用 refs?我认为这将是一个更好的做法。这就是你可以做的。
在类似
的组件内部创建一个状态变量
const [value, setValue] = useState();
将您的值和 setValue 函数作为输入作为
<input {...rest} value={value} onChange={e => setValue(e.target.value)} />
每当值发生变化时,将其传递给您的父组件,这样您的组件将 运行 本身作为一个模块化组件,您将只为您的输入组件提供一项工作。你可以这样做。将道具传递给您的输入组件,例如 onChange,它是一个函数:
useEffect(() => { props.onChange(value)}, [value])
将它们保存在父组件的状态中。
从状态变量中使用它而不是从引用中获取值。
我这样写是因为你正在使用 React,你也应该使用基于它们的组件 re-rendering。会更好use-case
正如 Cemil 所提到的,使用状态将是更常见的解决方案。但是如果你需要使用 refs 这样的东西,你只需要 forwardRef,不需要 useImperativeHandle:
const FormInput = ({ content, type, idFor, prefill }, ref) => {
return (
<div className="input-wrapper">
<label htmlFor={idFor}>{content}</label>
<input
ref={ref}
type={type}
id={idFor}
autoComplete="off"
defaultValue={prefill}
/>
</div>
)
}
export default forwardRef(FormInput)
// in Login:
fetchOrUpdateLogin({
email: emailValue.current.value,
password: passwordValue.current.value
})
在下面的登录表单中,我在提交功能中使用了 document.getElementById()
。它有效,但我今天听说这不是一个很好的实践,我最好改用 useRef()
。
但经过一些研究,useRef()
无法使用自定义组件,例如我的 <FormInput/>
。经过一些重新研究,我听说了 forwardRef()
和 useImperativeHandle()
它仍然有效,但我很困惑,我的代码似乎更复杂且更难管理。
您对此用例有何看法?
提前致谢
初始代码
function Login() {
const navigate = useNavigate()
const dispatch = useDispatch()
async function handleSubmit(e) {
e.preventDefault()
await dispatch(
fetchOrUpdateLogin({
email: document.getElementById('username').value,
password: document.getElementById('password').value,
})
)
navigate('/user')
}
return (
<main className="main bg-dark">
<section className="sign-in-content">
<i className="fa fa-user-circle sign-in-icon"></i>
<h1>Sign In</h1>
<form onSubmit={handleSubmit}>
<FormInput
content="Username"
type="text"
idFor="username"
prefill={USER.email}
/>
<FormInput
content="Password"
type="password"
idFor="password"
prefill={USER.password}
/>
<div className="input-remember">
<input type="checkbox" id="remember-me" />
<label htmlFor="remember-me">Remember me</label>
</div>
<Button type="submit" content="Sign In" classStyle="sign-in-button" />
</form>
</section>
</main>
)
}
function FormInput({ content, type, idFor, prefill }) {
return (
<div className="input-wrapper">
<label htmlFor={idFor}>{content}</label>
<input type={type} id={idFor} autoComplete="off" defaultValue={prefill} />
</div>
)
}
最终代码
function Login() {
const navigate = useNavigate()
const dispatch = useDispatch()
const emailValue = useRef(null)
const passwordValue = useRef(null)
async function handleSubmit(e) {
e.preventDefault()
await dispatch(
fetchOrUpdateLogin({
email: emailValue.current.inputValue(),
password: passwordValue.current.inputValue(),
})
)
navigate('/user')
}
return (
<main className="main bg-dark">
<section className="sign-in-content">
<i className="fa fa-user-circle sign-in-icon"></i>
<h1>Sign In</h1>
<form onSubmit={handleSubmit}>
<FormInput
content="Username"
type="text"
idFor="username"
prefill={USER.email}
ref={emailValue}
/>
<FormInput
content="Password"
type="password"
idFor="password"
prefill={USER.password}
ref={passwordValue}
/>
<div className="input-remember">
<input type="checkbox" id="remember-me" />
<label htmlFor="remember-me">Remember me</label>
</div>
<Button type="submit" content="Sign In" classStyle="sign-in-button" />
</form>
</section>
</main>
)
}
const FormInput = ({ content, type, idFor, prefill }, ref) => {
const valueRef = useRef(null)
useImperativeHandle(ref, () => ({
inputValue() {
return valueRef.current.value
},
}))
return (
<div className="input-wrapper">
<label htmlFor={idFor}>{content}</label>
<input
ref={valueRef}
type={type}
id={idFor}
autoComplete="off"
defaultValue={prefill}
/>
</div>
)
}
export default forwardRef(FormInput)
我就是想问这个。为什么不尝试基于状态的方法而不是使用 refs?我认为这将是一个更好的做法。这就是你可以做的。
在类似
的组件内部创建一个状态变量const [value, setValue] = useState();
将您的值和 setValue 函数作为输入作为
<input {...rest} value={value} onChange={e => setValue(e.target.value)} />
每当值发生变化时,将其传递给您的父组件,这样您的组件将 运行 本身作为一个模块化组件,您将只为您的输入组件提供一项工作。你可以这样做。将道具传递给您的输入组件,例如 onChange,它是一个函数:
useEffect(() => { props.onChange(value)}, [value])
将它们保存在父组件的状态中。
从状态变量中使用它而不是从引用中获取值。
我这样写是因为你正在使用 React,你也应该使用基于它们的组件 re-rendering。会更好use-case
正如 Cemil 所提到的,使用状态将是更常见的解决方案。但是如果你需要使用 refs 这样的东西,你只需要 forwardRef,不需要 useImperativeHandle:
const FormInput = ({ content, type, idFor, prefill }, ref) => {
return (
<div className="input-wrapper">
<label htmlFor={idFor}>{content}</label>
<input
ref={ref}
type={type}
id={idFor}
autoComplete="off"
defaultValue={prefill}
/>
</div>
)
}
export default forwardRef(FormInput)
// in Login:
fetchOrUpdateLogin({
email: emailValue.current.value,
password: passwordValue.current.value
})