首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >编写通用的反应性输入钩子

编写通用的反应性输入钩子
EN

Stack Overflow用户
提问于 2019-11-25 08:30:31
回答 1查看 719关注 0票数 2

我正在尝试编写一个通用的,以允许我更新对象。

我参考了: Input (来源:https://rangle.io/blog/simplifying-controlled-inputs-with-hooks/)并做了一些修改:

代码语言:javascript
复制
import { useState } from "react";

export const useForm = initialObject => {
  const [values, setValues] = useState(initialObject);

  return {
    values: values || initialObject,
    setValues,
    reset: () => setValues({}),
    bind: {
      onChange: (event) => {
        setValues({
          ...values,
          [event.target.id]: event.target.value
        })
      }
    }
  };
};

这在单层对象中运行得很好:

代码语言:javascript
复制
{ name: '', type: '' }

但是对于具有嵌套值的对象:

代码语言:javascript
复制
{ name: '', type: '', price: { dollar: 5, cents: 20  } }

我太确定该如何替换[event.target.id]来读取嵌套级别的对象。

有人能给我建议吗?

更新:

代码语言:javascript
复制
import { useState } from "react";

export const useForm = initialObject => {
  const [values, setValues] = useState(initialObject);

  return {
    values: values || initialObject,
    setValues,
    reset: () => setValues({}),
    bind: {
      onChange: (event) => {
        // ###need make this part generic###
        // event.target.id will be "price.dollar"
        values['price']['dollar'] = event.target.value;
        setValues({
          ...values
        })
      }
    }
  };
};
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-11-25 08:48:34

通常,您的钩子应该接受namevalue来更新本地状态。显然,您的钩子总是接收一个event,您提取event.target.id作为字段的nameevent.target.value作为字段的值。我建议您更新钩子以接收namevalue作为参数,并让使用钩子的组件定义什么是namevalue

根据您的钩子,您可以像这样更新嵌套对象。请看这个例子。

代码语言:javascript
复制
import React, { useState } from "react";
import ReactDOM from "react-dom";

const useForm = initialObject => {
  const [values, setValues] = useState(initialObject);

  return {
    values: values || initialObject,
    setValues,
    reset: () => setValues({}),
    bind: {
      onChange: event => {
        setValues({
          ...values,
          [event.target.id]: event.target.value
        });
      }
    }
  };
};

const App = () => {
  const { values, bind } = useForm({
    name: "",
    type: "",
    price: { dollar: 5, cents: 20 }
  });
  return (
    <div>
      Hook state:
      <pre>{JSON.stringify(values, null, 4)}</pre>
      <div>
        <div>
          <label>
            Name : <br />
            <input id="name" onChange={bind.onChange} />
          </label>
        </div>
        <div>
          <label>
            Type : <br />
            <input id="type" onChange={bind.onChange} />
          </label>
        </div>
        <div>
          <label>
            Price - Dollar : <br />
            <input
              id="dollar"
              type="number"
              onChange={e => {
                bind.onChange({
                  target: {
                    id: "price",
                    value: { ...values.price, [e.target.id]: e.target.value }
                  }
                });
              }}
            />
          </label>
        </div>
        <div>
          <label>
            Price - Cents : <br />
            <input
              id="cents"
              type="number"
              onChange={e => {
                bind.onChange({
                  target: {
                    id: "price",
                    value: { ...values.price, [e.target.id]: e.target.value }
                  }
                });
              }}
            />
          </label>
        </div>
      </div>
    </div>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

更新(11/25/2019):

但是,您可以按以下方式更新钩子

代码语言:javascript
复制
const useForm = initialObject => {
  const [values, setValues] = useState(initialObject);

  return {
    values: values || initialObject,
    setValues,
    reset: () => setValues({}),
    bind: {
      onChange: event => {
        setValues({
          ...values,
          [event.target.id]: event.target.value
        });
      },
      onNestedChange: (event, name) => {
        setValues({
          ...values,
          [name]: {
            ...values[name],
            [event.target.id]: event.target.value,
          }
        })
      }
    }
  };
};

然后,在您的输入中,可以如下所示:

代码语言:javascript
复制
<div>
  <label>
    Price - Dollar : <br />
    <input
      id="dollar"
      type="number"
      onChange={e => bind.onNestedChange(e, 'price')}
    />
  </label>
</div>
<div>
  <label>
    Price - Cents : <br />
    <input
      id="cents"
      type="number"
      onChange={e => bind.onNestedChange(e, 'price')}
    />
  </label>
</div>

这样,您就为嵌套对象创建了另一个绑定方法,也许还可以添加另一个名为array之类的方法。希望这能给你一些关于如何改进钩子的想法。顺便说一句,有很多可以做到这一点,这只是一个例子。也许有更好的方法来做到这一点。

更新2 (11/25/2019):

我更新了您的useForm钩子,现在可以将嵌套对象属性设置为您的状态。但是,我还没有用数组进行测试,这可能会导致问题。

代码语言:javascript
复制
const useForm = initialObject => {
  const [values, setValues] = useState(initialObject);
  // Copied and modified from https://stackoverflow.com/a/18937118/11125492
  const nestedObjectSet = (obj, path, value) => {
    let schema = obj; // a moving reference to internal objects within obj
    const pList = path.split(".");
    const len = pList.length;
    for (let i = 0; i < len - 1; i++) {
      let elem = pList[i];
      if (!schema[elem]) schema[elem] = {};
      schema = schema[elem];
    }
    schema[pList[len - 1]] = value;
  };
  // handleOnChange update state value
  const handleOnChange = event => {
    let newValues = Object.assign({}, values);
    nestedObjectSet(newValues, event.target.name, event.target.value);
    setValues(newValues);
  };
  return {
    values: values || initialObject,
    setValues,
    reset: () => setValues({}),
    bind: {
      onChange: handleOnChange
    }
  };
};

你可以这样用它。注意,我已经将对象的keyevent.target.id更改为event.target.namekey应该用name而不是id设置

代码语言:javascript
复制
const App = () => {
  const { values, bind } = useForm({
    name: "",
    type: "",
    price: { dollar: 5, cents: 20 }
  });
  return (
    <div>
      Hook state:
      <pre>{JSON.stringify(values, null, 4)}</pre>
      <div>
        <div>
          <label>
            Name : <br />
            <input name="name" {...bind} />
          </label>
        </div>
        <div>
          <label>
            Type : <br />
            <input name="type" {...bind} />
          </label>
        </div>
        <div>
          <label>
            Price - Dollar : <br />
            <input name="price.dollar" type="number" {...bind} />
          </label>
        </div>
        <div>
          <label>
            Price - Cents : <br />
            <input name="price.cents" type="number" {...bind} />
          </label>
        </div>
      </div>
    </div>
  );
};

沙箱演示链接:https://codesandbox.io/s/react-useform-hook-nested-object-cqn9j?fontsize=14&hidenavigation=1&theme=dark

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59027749

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档