当前位置: 首页 > 知识库问答 >
问题:

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

松旻
2023-03-14

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

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

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

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

共有2个答案

冷宏茂
2023-03-14

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

两个inputs设置原子注册表项的状态,使其相互覆盖。

您需要做的是将一个对象作为寄存器的值保存,该对象有两个键:emailphone。然后,您可以使用已更改的特定输入中的相关值更新每个键。

所以。而不是:

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

创建这个atom

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

这为电子邮件电话创建了初始值为空字符串的对象。

现在,您可以像这样定义set函数:

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

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

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

单于扬
2023-03-14

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

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

然而,惯用的方法是使用选择器将原子组成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);
  },
});

下面是一个完整且独立的示例片段,您可以在此页面上运行该示例来验证它是否有效:

注意:它使用UMD版本的ReactReactDOMRecoil,因此它们是使用这些名称而不是使用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>

 类似资料:
  • 我对从我的组件调用异步操作有问题,我想我做了工作所需的一切,但似乎没有,我使用了: 将调度映射到属性 在里面我回来了 操作:bindActionCreators(fetchPosts,调度) 我把它连接起来。 在所有这些事情之后,我试着在我的组件中调用这个动作- this . props . actions . fetch post() 结果我在控制台中收到这个错误- this.props.act

  • 问题内容: 我正在使用通过上下文传递的函数。 现在我用。这可行。 如果我需要来自两个不同父组件的函数,该怎么办? 问题答案: 您仍然可以通过16.3 Context API来使用子级功能消费者节点,这是React文档建议的做法: 要在组件的上下文中使用函数,通常将组件包装在HOC中,以便将上下文作为prop传递: 如果您正在运行React 16.8+,则还可以使用钩子更干净地执行此操作,而无需使用

  • 使用ASP.NET MVC,我正在尝试使用Chart.js库创建一个在单个页面上显示多个条形图的网页。每个图表都有不同的数据,并且是在其自己的部分视图中创建的。虽然没有创建多个不同的图表,但最后一个图表数据覆盖了页面上的第一个图表,所有剩余的图表都显示为空白。如下图所示,“标题和分段”数据和图例覆盖了“制作人”数据,“标题和分段”图表为空。 在主视图页面上,我使用foreach循环为每个报表呈现相

  • 我想通过两个下拉列表获取用户对表单的输入,并使用react将其存储在全局变量中。我查看了reacts文档,了解了如何创建表单,并稍微处理了一下它们的代码,使其具有两个下拉列表,但无法将变量保存为全局变量,并将该全局变量打印到屏幕上。不幸的是,当我点击第二个提交按钮时出现了一个错误(第一个按钮什么也没做)。这里有一个错误:TypeError:这是未定义的handleSubmit src/App。js

  • 问题内容: 我在构建webapp时遇到了这个问题,并在jsfiddle中复制了它。本质上,每次输入内容时,我都希望调用一个输入,然后将其传递到父App类中,然后将其重新呈现到Message类中。但是输出似乎总是比我实际输入的内容落后一步。jsfiddle演示应该可以自我解释。我想知道我是否做错了任何提示。 html js 问题答案: 呼叫不同步。它创建一个“待处理状态转换”。(有关更多详细信息,请

  • 我最近使用钩子编写了一个表组件,每次页面加载时都会有一个对后端的API调用,因此同时会显示一个加载微调器,直到有来自API的响应为止。我使用redux作为状态管理,所以当有来自API的响应时,将调度一个操作并更新状态。所以这里的问题是,通常在类组件中,我们可以使用 但是我不知道如何使用钩子实现同样的效果。我还提到了这个stackoverflow问题如何比较反应挂钩的旧值和新值使用效果? 但这个解决