reactjs - Recoil 中的动态原子键

标签 reactjs recoiljs

我正在尝试创建一个动态表单,其中表单输入字段是根据 API 返回的数据呈现的。

由于 atom 需要有一个唯一的键,我尝试将它包装在一个函数中,但每次我更新字段值或组件重新安装(尝试更改选项卡)时,我都会收到一条警告:

""

我在这里做了一个小的运行例子https://codesandbox.io/s/zealous-night-e0h4jt?file=/src/App.tsx (与下面相同的代码):

import React, { useEffect, useState } from "react";
import { atom, RecoilRoot, useRecoilState } from "recoil";
import "./styles.css";

const textState = (key: string, defaultValue: string = "") =>
  atom({
    key,
    default: defaultValue
  });

const TextInput = ({ id, defaultValue }: any) => {
  const [text, setText] = useRecoilState(textState(id, defaultValue));

  const onChange = (event: any) => {
    setText(event.target.value);
  };

  useEffect(() => {
    return () => console.log("TextInput unmount");
  }, []);

  return (
    <div>
      <input type="text" value={text} onChange={onChange} />
      <br />
      Echo: {text}
    </div>
  );
};

export default function App() {
  const [tabIndex, setTabIndex] = useState(0);

  // This would normally be a fetch request made by graphql or inside useEffect
  const fields = [
    { id: "foo", type: "text", value: "bar" },
    { id: "hello", type: "text", value: "world" }
  ];

  return (
    <div className="App">
      <RecoilRoot>
        <form>
          <button type="button" onClick={() => setTabIndex(0)}>
            Tab 1
          </button>
          <button type="button" onClick={() => setTabIndex(1)}>
            Tab 2
          </button>

          {tabIndex === 0 ? (
            <div>
              <h1>Fields</h1>
              {fields.map((field) => {
                if (field.type === "text") {
                  return (
                    <TextInput
                      key={field.id}
                      id={field.id}
                      defaultValue={field.value}
                    />
                  );
                }
              })}
            </div>
          ) : (
            <div>
              <h1>Tab 2</h1>Just checking if state is persisted when TextInput
              is unmounted
            </div>
          )}
        </form>
      </RecoilRoot>
    </div>
  );
}

这甚至可以通过后坐力实现。我的意思是它似乎有效,但我不能忽略警告。

最佳答案

This answer展示了如何使用记忆化手动管理原子的多个实例。

但是,如果您的每个使用实例的defaultValue 都不会改变,那么 Recoil 已经提供了一个可以为您处理创建和内存的实用程序:atomFamily .我将从上一个链接中引用一些相关信息(但请阅读所有内容以充分理解):

... You could implement this yourself via a memoization pattern. But, Recoil provides this pattern for you with the atomFamily utility. An Atom Family represents a collection of atoms. When you call atomFamily it will return a function which provides the RecoilState atom based on the parameters you pass in.

The atomFamily essentially provides a map from the parameter to an atom. You only need to provide a single key for the atomFamily and it will generate a unique key for each underlying atom. These atom keys can be used for persistence, and so must be stable across application executions. The parameters may also be generated at different callsites and we want equivalent parameters to use the same underlying atom. Therefore, value-equality is used instead of reference-equality for atomFamily parameters. This imposes restrictions on the types which can be used for the parameter. atomFamily accepts primitive types, or arrays or objects which can contain arrays, objects, or primitive types.

这是一个工作示例,展示了在使用 的实例时如何使用 iddefaultValue(作为元组的值的唯一组合)作为参数>atomFamily 每个输入的状态:

TS Playground

body { font-family: sans-serif; }
input[type="text"] { font-size: 1rem; padding: 0.5rem; }
<div id="root"></div><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.6.1/umd/recoil.min.js"></script><script src="https://unpkg.com/@babel/standalone@7.17.7/babel.min.js"></script><script>Babel.registerPreset('tsx', {presets: [[Babel.availablePresets['typescript'], {allExtensions: true, isTSX: true}]]});</script>
<script type="text/babel" data-type="module" data-presets="tsx,react">

// import ReactDOM from 'react-dom';
// import type {ReactElement} from 'react';
// import {atomFamily, RecoilRoot, useRecoilState} from 'recoil';

// This Stack Overflow snippet demo uses UMD modules instead of the above import statments
const {atomFamily, RecoilRoot, useRecoilState} = Recoil;

const textInputState = atomFamily<string, [id: string, defaultValue?: string]>({
  key: 'textInput',
  default: ([, defaultValue]) => defaultValue ?? '',
});

type TextInputProps = {
  id: string;
  defaultValue?: string;
};

function TextInput ({defaultValue = '', id}: TextInputProps): ReactElement {
  const [value, setValue] = useRecoilState(textInputState([id, defaultValue]));

  return (
    <div>
      <input
        type="text"
        onChange={ev => setValue(ev.target.value)}
        placeholder={defaultValue}
        {...{value}}
      />
    </div>
  );
}

function App (): ReactElement {
  const fields = [
    { id: 'foo', type: 'text', value: 'bar' },
    { id: 'hello', type: 'text', value: 'world' },
  ];

  return (
    <RecoilRoot>
      <h1>Custom defaults using atomFamily</h1>
      {fields.map(({id, value: defaultValue}) => (
        <TextInput key={id} {...{defaultValue, id}} />
      ))}
    </RecoilRoot>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

</script>

关于reactjs - Recoil 中的动态原子键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71527067/

相关文章:

reactjs - 如何添加一串数据属性来 react ?

reactjs - "Did not retain recoil value on render, or committed after timeout elapsed. This is fine, but odd. undefined"是什么意思?我该如何解决?

reactjs - react-table 迭代对象数组以打印列中的值

javascript - useEffect 中的 inputFile.current.click() 在 safari 中不起作用

javascript - 有没有办法在组件之外更新 recoilJS 的状态?

reactjs - React/Recoil - setAtom 新值何时可用?

reactjs - 如何使用 Recoil 在 React 组件之外操作全局状态?

reactjs - 如何在不丢失流类型信息的情况下继承自定义 React 组件?

reactjs - 如何渲染同级元素而不将它们包装在父标签中?

javascript - Reactjs 和 Flexbox : Wrapping divs in render function making flex hard