gpt4 book ai didi

reactjs - Recoil 中的动态原子键

转载 作者:行者123 更新时间:2023-12-02 01:41:41 27 4
gpt4 key购买 nike

我正在尝试创建一个动态表单,其中表单输入字段是根据 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/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com