【问题标题】:React State and Events managed by Child Components由子组件管理的 React 状态和事件
【发布时间】:2022-10-24 11:56:49
【问题描述】:

子组件使用回调函数管理父对象的状态。代码打击只适用于一个变量,但在处理对象时会出现错误。我得到的错误是在向文本区域输入值时..

备注.map 不是函数

请帮我解决这个问题。

另外,请让我知道这里的 Ref 是否有用。谢谢你。


  return (
    <div className="container">
      {remarks?.map((items: any) => {
        return (
          <div key={items?.id}>
            <label>
              <textarea
                name="remarkVal"
                id={items?.id}
                onChange={(e) => onSliderChangeHandler(e)}
                value={items?.remarksVal}
                ref={childRef}
                placeholder={placeholder}
              />
            </label>
          </div>
        );
      })}
    </div>
  );
};

根据答案获得一个新行来编辑代码。

 setChildState((prevState: any) => [
          ...prevState,
          { [e.target.name]: e.target.value }
        ]);

【问题讨论】:

  • 这没有任何意义:const [remarks, setRemarks] = useState(remarks); 你如何使用变量来创建自己?
  • 那是一个错误,我已经更新了我的问题。请帮忙。谢谢

标签: reactjs typescript react-hooks


【解决方案1】:

您的状态值是一个数组:

const [remarks, setRemarks] = useState( [{ id: 1, remarkVal: "hello man" }]);

当您在此处更新状态时,将其更改为目的

setChildState((prevState: any) => ({
  ...prevState,
  [e.target.name]: e.target.value
}));

如错误所述,map() 不是对象上的函数。将您的状态值保留为数组。例如,您可以向其附加一个元素:

setChildState((prevState: any) => ([
  ...prevState,
  e.target.value
]));

或者也许修改(替换)数组中的单个项目:

setChildState((prevState: any) => ([{
  ...prevState[0],
  [e.target.name]: e.target.value
}]));

(笔记:这个假设该数组将始终只有一项。尽管无论如何您似乎都在做出这种假设。维护一个只有一个项目的数组是很奇怪的。)

或者也许是数组将要包含多个项目,并且您想更新一个特定的项目?您在onSliderChangeHandler 内部对.map() 的奇怪使用可能试图暗示这一点。在这种情况下,您可能想要这样的东西:

const onSliderChangeHandler = (e: any) => {
  setChildState((prevState: any) => ([
    ...prevState.map(p => {
      if (p.id === e.target.id) {
        return { ...p, [e.target.name]: e.target.value };
      } else {
        return p;
      }
    });
  ]));
};

请注意如何,而不是映射到数组以将状态更新为只有一项,状态更新为结果数组映射操作,其中目标项目被替换(并且所有其他项目按原样返回)。


基本上,您需要做的是退后一步并检查/理解您正在使用的数据结构。它应该是一个对象还是一个对象数组?为什么?进行更新时,应该更改什么?为什么?不要只是为了“让它工作”而进行随机更改,要刻意维护您想要维护的数据。

【讨论】:

  • 父级有 5 个对象。因此,有 5 个子表单组件。一个这样的 Form 组件是 Remarks 组件,其中显示了两个文本区域。因此,我尝试映射备注对象并在子组件中显示两个文本区域。因此,在加载时,父组件会将带有/不带数据的对象传递给所有子组件。由于这是一个表单,因此用户可能想要编辑内容。因此,我正在尝试管理子组件中的状态和事件并将其传递回父组件。父组件在单击保存按钮时保存所有 5 个对象。
  • 我修好了here
  • 这是我一段时间以来见过的最大的意大利面。子组件根本不应该有状态
  • @Peter:我已经更新了答案。总体而言,示例和描述对于预期的功能非常混乱。但最终这一切都取决于您如何更新onSliderChangeHandler 中的状态。仔细检查您是如何更新状态的,这是一个好主意。
【解决方案2】:

康拉德·林科夫斯基 链接:https://codesandbox.io/s/relaxed-fog-8nfnhb?file=/src/App.js

export default function App() {
  const [Form, setForm] = useState(Details);
  const [remarks, setRemarks] = useState([{ id: 1, remarksVal: "hello man" }]);
  const [parentState, setParentState] = useState(123);
  // make wrapper function to give child
  const wrapperSetParentState = useCallback(
    (val) => {
      setParentState(val);
    },
    [setParentState]
  );
  const setterRemarks = useCallback(
    (val) => {
      setRemarks(val);
    },
    [setRemarks]
  );

  useEffect(() => {
    console.log("parent", remarks);
  }, [remarks]);

  return (
    <div className="App">
      <RemarksPage
        remarks={remarks}
        setterRemarks={setterRemarks}
        placeholder="I am remarks section..."
      />
      <div style={{ margin: 30 }}>
        <Child
          parentState={parentState}
          parentStateSetter={wrapperSetParentState}
        />
        <br />
        {parentState}
      </div>
    </div>
  );
}

import { Key, useEffect, useRef, useState } from "react";

export default ({ remarks, placeholder, setterRemarks, ...rest }) => {
  const childRef = useRef();
  const [childState, setChildState] = useState(remarks);

  useEffect(() => {
    setterRemarks(childState);
  }, [setterRemarks, childState]);

  const onSliderChangeHandler = (id: Key | null | undefined, value: string) => {
    setChildState((remarks: any[]) =>
      remarks.map((remark) =>
        remark.id === id ? { ...remark, remarksVal: value } : remark
      )
    );
  };

  return (
    <div className="container">
      {remarks?.map(
        (items: {
          id: Key | null | undefined;
          remarksVal: string | number | readonly string[] | undefined;
        }) => {
          return (
            <div key={items?.id}>
              <label>
                <textarea
                  name="remarkVal"
                  id={items?.id}
                  onChange={(e) =>
                    onSliderChangeHandler(items.id, e.target.value)
                  }
                  value={items?.remarksVal}
                  ref={childRef}
                  placeholder={placeholder}
                />
              </label>
            </div>
          );
        }
      )}
    </div>
  );
};

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 2017-09-16
  • 1970-01-01
  • 2018-05-24
  • 1970-01-01
  • 2017-01-08
  • 2020-06-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多