一个反应反冲原子中的多个形式值相互覆盖



有没有办法在一个React Recoil原子中保存多个表单输入值?我一直试图添加两个表单字段值,但它们只是相互覆盖。

我有一张有两个字段的登记表;电子邮件和电话。

我的(简化的(表单组件看起来是这样的;

import { atom, useSetRecoilState, useRecoilValue } from 'recoil';
const registerAtom = atom({
key: 'register',
default: [],
});
function Registration() {
const setEmail = useSetRecoilState(registerAtom);
const email = useRecoilValue(registerAtom);
const setPhone = useSetRecoilState(registerAtom);
const phone = useRecoilValue(registerAtom);
return (
<>
<form>
<input name="email" type="text" className="form-control" value={email} onChange={e => setEmail(e.target.value)} placeholder="Email Address" />
<input name="phone" type="text" className="form-control" value={phone} onChange={e => setPhone(e.target.value)} placeholder="Phone Number" />
</form>
</>
)
}

如果你确信你永远不需要独立地读或写电子邮件和电话状态,一个简单的方法是使用一个带有对象值的原子(这相当于使用React的useState钩子和一个对象值(:

import {atom} from 'recoil';
const contactInfoState = atom({
key: 'contactInfo',
default: {
email: '',
phone: '',
},
});

然后,像这样使用(每次更新整个对象(:

import {useRecoilState} from 'recoil';
function Registration () {
const [{email, phone}, setContactInfo] = useRecoilState(contactInfoState);

return (
<form>
<input
type="text"
value={email}
onChange={ev => setContactInfo({email: ev.target.value, phone})}
placeholder="Email Address"
/>
<input
type="text"
value={phone}
onChange={ev => setContactInfo({email, phone: ev.target.value})}
placeholder="Phone Number"
/>
</form>
)
}

然而,实现这一点的惯用方法(以及Recoil变得更强大的地方(是使用selector合成atom,这可以提供一种一起读写值的方法(就像上面的例子一样(,但仍然允许使用原子独立地读写它们:

import {atom, DefaultValue, selector} from 'recoil';
const emailState = atom({
key: 'email',
default: '',
});
const phoneState = atom({
key: 'phone',
default: '',
});
const contactInfoState = selector({
key: 'contactInfo',
get: ({get}) => {
// get values from individual atoms:
const email = get(emailState);
const phone = get(phoneState);
// then combine into desired shape (object) and return:
return {email, phone};
},
set: ({set}, value) => {
// in a Reset action, the value will be DefaultValue (read more in selector docs):
if (value instanceof DefaultValue) {
set(emailState, value);
set(phoneState, value);
return;
}
// otherwise, update individual atoms from new object state:
set(emailState, value.email);
set(phoneState, value.phone);
},
});

下面是一个完整的、自包含的片段示例,您可以在该页面上运行该示例来验证它是否有效:

注意:它使用ReactReactDOMRecoil的UMD版本,因此它们使用这些名称而不是使用import语句全局公开。

<script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/recoil@0.5.2/umd/recoil.min.js"></script>
<script src="https://unpkg.com/@babel/standalone@7.16.3/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel" data-type="module" data-presets="react">
const {
atom,
DefaultValue,
RecoilRoot,
selector,
useRecoilValue,
useSetRecoilState,
} = Recoil;
const emailState = atom({
key: 'email',
default: '',
});
const phoneState = atom({
key: 'phone',
default: '',
});
const contactInfoState = selector({
key: 'contactInfo',
get: ({get}) => {
const email = get(emailState);
const phone = get(phoneState);
return {email, phone};
},
set: ({set}, value) => {
if (value instanceof DefaultValue) {
set(emailState, value);
set(phoneState, value);
return;
}
set(emailState, value.email);
set(phoneState, value.phone);
},
});
function Registration () {
const {email, phone} = useRecoilValue(contactInfoState);
const setEmail = useSetRecoilState(emailState);
const setPhone = useSetRecoilState(phoneState);

return (
<form>
<input
type="text"
value={email}
onChange={ev => setEmail(ev.target.value)}
placeholder="Email Address"
/>
<input
type="text"
value={phone}
onChange={ev => setPhone(ev.target.value)}
placeholder="Phone Number"
/>
</form>
)
}
function DisplayState () {
const email = useRecoilValue(emailState);
const phone = useRecoilValue(phoneState);
return (
<pre>
<code>{JSON.stringify({email, phone}, null, 2)}</code>
</pre>
);
}
function Example () {
return (
<RecoilRoot>
<Registration />
<DisplayState />
</RecoilRoot>
);
}
ReactDOM.render(<Example />, document.getElementById('root'));
</script>

您的atom只有一个值,register在开始时保存一个数组,然后分配输入的值。

两个input都设置原子registerAtom的状态,使其相互覆盖。

您需要做的是保持一个对象作为register的值,它有两个键:emailphone。然后,您可以使用已更改的特定input中的相关值更新每个密钥。

所以。而不是:

const registerAtom = atom({
key: 'register',
default: [],
});

创建此atom:

const registerAtom = atom({
key: 'register',
default: {
email: '',
phone: ''
},
});

这为emailphone创建了具有空字符串初始值的对象。

现在定义set函数如下:

const setRegistrationInfo = useSetRecoilState(registerAtom);
const registrationInfo = useRecoilValue(registerAtom);

最后,您所需要做的就是在设置对象的状态时更改对象的特定键。确保您正在创建一个新的Object,因为您正在更新一个状态,并且该状态需要一个新更新的对象,所以我们将使用Object.assign:

<form>
<input name="email" type="text" className="form-control" value={registrationInfo.email} onChange={e => setRegistrationInfo(Object.assign(registrationInfo, {email: e.target.value}))} placeholder="Email Address" />
<input name="phone" type="text" className="form-control" value={registrationInfo.phone} onChange={e => setRegistrationInfo(Object.assign(registrationInfo, {phone: e.target.value}))} placeholder="Phone Number" />
</form>

最终代码:

import { atom, useSetRecoilState, useRecoilValue } from 'recoil';
const registerAtom = atom({
key: 'register',
default: {
email: '',
phone: ''
},
});
function Registration() {
const setRegistrationInfo = useSetRecoilState(registerAtom);
const registrationInfo = useRecoilValue(registerAtom);
return (
<>
<form>
<input name="email" type="text" className="form-control" value={registrationInfo.email} onChange={e => setRegistrationInfo(Object.assign(registrationInfo, {email: e.target.value}))} placeholder="Email Address" />
<input name="phone" type="text" className="form-control" value={registrationInfo.phone} onChange={e => setRegistrationInfo(Object.assign(registrationInfo, {phone: e.target.value}))} placeholder="Phone Number" />
</form>
</>
)
}

相关内容

  • 没有找到相关文章

最新更新