【问题标题】:Add input data to deep object React.JS将输入数据添加到深度对象 React.JS
【发布时间】:2020-06-14 12:25:46
【问题描述】:

我正在尝试根据用户输入创建对象。我一直关注到现在。

它给了我以下错误:

未捕获的类型错误:无法读取未定义的属性“全名”

const App = () => {

  let passengerObj = {
        "primary": {
            "fullname": "",
            "age": null,
        },
        "secondary": [],
    }
    
    const [passengerdata, setPassengerData] = useState(passengerObj)

        return (
            <form className="passenger-form">
                    <input 
                        type="text" 
                        placeholder="Full Name" 
                        value={passengerdata.primary.fullname}
                        onChange={setPassengerData}
                        required/>
                    <input 
                        type="number" 
                        placeholder="Age" 
                        value={passengerdata.primary.age || ""}
                        onChange={setPassengerData}
                        required/>
                  <div className="secondary-passenger-data">
                        <input 
                            type="text" 
                            placeholder="Full Name of Passenger2"
                            required/>
                        <input type="number"
                            placeholder="Age Passenger2"
                            required/>
                   </div>
                   <div className="secondary-passenger-data">
                        <input 
                            type="text" 
                            placeholder="Full Name of Passenger3"
                            required/>
                        <input type="number"
                            placeholder="Age Passenger3"
                            required/>
                   </div>
             </form>
  )}

当我尝试在passengerObj 中添加主要和次要数据时。所以,我可以保存它。但是,它想让我这样做。

不确定如何使用钩子将主要和辅助数据添加到对象。

这里的沙箱我复制它。

https://codesandbox.io/s/distracted-sunset-89ecj?file=/src/App.js:0-1184

任何将不胜感激。

【问题讨论】:

  • 在直接从 onChange 调用 setPassengerData 时,您期望什么?我建议进行基本调试 - console.log(passengerData)。
  • @jonrsharpe 我用粗体突出了我的期望。
  • 但你并没有试图这样做。您将使用 onChange 的默认参数替换整个状态。再一次,做一些基本的调试,你很快就会明白为什么passengerData.primary 变得未定义。这与 React 或 hooks 并没有真正的关系,同样的事情也会发生在带有常规赋值的 vanilla JS 中。

标签: javascript reactjs react-hooks


【解决方案1】:

写一个通用的onChangeHandler,它足够动态以更新多个输入元素。

为您的输入提供一个名称。向处理程序提供类型(主要/次要)和事件。

Updated working demo

处理程序

const handleChange = (e, type) => {
    const { target } = e;
    setPassengerData(prev => ({
      ...prev,
      [type]: {
        ...prev[type],
        [target.name]: target.value
      }
    }));
  };

JSX

<input
        type="text"
        placeholder="Full Name"
        value={passengerdata.primary.fullname}
        onChange={e => handleChange(e, "primary")}
        name="fullname"
        required
      />

【讨论】:

    【解决方案2】:

    当您为fullnameage 输入一些数据时,代码中实际发生了什么,整个状态对象被事件对象覆盖。因此,该对象将没有任何名为 primary 的属性,因此最终当您尝试访问 fullname 时,它会出现错误。

    请使用以下代码更新您的代码。我添加了两种方法来更新状态中的fullnameage

    let passengerObj = {
      "primary": {
          "fullname": "",
          "age": null,
      },
      "secondary": [],
    }
    
    const [passengerdata, setPassengerData] = useState(passengerObj)
    
    const setAge = (age) => {
      setPassengerData({
        ...passengerdata,
        primary: {
          ...passengerdata.primary,
          "age": age
        }
      })
    }
    
    const setFullname = (name) => {
      setPassengerData({
        ...passengerdata,
        primary: {
          ...passengerdata.primary,
          "fullname": name
        }
      })
    }
      return (
          <form className="passenger-form">
                  <input 
                      type="text" 
                      placeholder="Full Name" 
                      value={passengerdata.primary.fullname}
                      onChange={(e) => setFullname(e.target.value)}
                      required/>
                  <input 
                      type="number" 
                      placeholder="Age" 
                      value={passengerdata.primary.age || ""}
                      onChange={(e) => setAge(e.target.value)}
                      required/>
                <div className="secondary-passenger-data">
                      <input 
                          type="text" 
                          placeholder="Full Name of Passenger2"
                          required/>
                      <input type="number"
                          placeholder="Age Passenger2"
                          required/>
                 </div>
                 <div className="secondary-passenger-data">
                      <input 
                          type="text" 
                          placeholder="Full Name of Passenger3"
                          required/>
                      <input type="number"
                          placeholder="Age Passenger3"
                          required/>
                 </div>
           </form>
    )}
    

    这是另一个版本,它是用于更新任何输入字段的通用方法。

    let passengerObj = {
      "primary": {
          "fullname": "",
          "age": null,
      },
      "secondary": [],
    }
    
    const [passengerdata, setPassengerData] = useState(passengerObj);
    
    const setFormData = (name, value) => {
      setPassengerData(passengerData => {
        ...passengerData,
        primary: {
          ...passengerData.primary,
          [name]: value,
        }
      })
    }
    
    return (
          <form className="passenger-form">
                  <input 
                      name="fullname"
                      type="text" 
                      placeholder="Full Name" 
                      value={passengerdata.primary.fullname}
                      onChange={(e) => setFormData(e.target.name, e.target.value)}
                      required/>
                  <input 
                      name="age"
                      type="number" 
                      placeholder="Age" 
                      value={passengerdata.primary.age || ""}
                      onChange={(e) => setFormData(e.target.name, e.target.value)}
                      required/>
                <div className="secondary-passenger-data">
                      <input 
                          type="text" 
                          placeholder="Full Name of Passenger2"
                          required/>
                      <input type="number"
                          placeholder="Age Passenger2"
                          required/>
                 </div>
                 <div className="secondary-passenger-data">
                      <input 
                          type="text" 
                          placeholder="Full Name of Passenger3"
                          required/>
                      <input type="number"
                          placeholder="Age Passenger3"
                          required/>
                 </div>
           </form>
    )
    
    

    希望这会有所帮助。

    【讨论】:

    • e.target.value 已关闭,检查您的答案,尝试输入一些内容
    • 这工作得很好,我没有看到任何问题。我在问题中提供的沙箱中尝试了相同的代码,并且按预期工作。
    • 你是对的,我的错,我认为这是 setPassengerData(prev =&gt; ...) 功能更新,但是,这个答案仍然会在每次渲染时重新创建所有函数,我赞成,因为这是一个有效的答案
    • 我同意在每次渲染时都会重新创建所有函数的观点,但是除非使用useCallback 会导致如此多的性能问题,否则它也有其自身的缺点,所以没有使用它版本。
    • 只有几个缺点,它会在每次渲染中重新创建所有函数(不是那么重要),由于passengerdata 上的闭包,您将无法重用这些函数(如setAge),这意味着您不能将其作为回调传递给某处。但是可以肯定的是,对于更改输入,它是一个有效的答案
    【解决方案3】:

    这可能有帮助

    function App() {
      let passengerObj = {
        "primary": {
          "fullname": "",
          "age": null,
        },
        "secondary": [],
      };
    
      const [passengerdata, setPassengerData] = React.useState(passengerObj);
    
      return (
        <form className="passenger-form">
          <input
            type="text"
            placeholder="Full Name"
            value={passengerdata.primary.fullname}
            onChange={(e) => {
              const {value} = e.target;
              setPassengerData((passengerdata) => ({
                ...passengerdata,
                primary: {
                  ...passengerdata.primary,
                  fullname: value
                }
              }));
            }}
            required/>
          <input
            type="number"
            placeholder="Age"
            value={passengerdata.primary.age || ""}
            onChange={() => {
    
            }}
            required/>
          <div className="secondary-passenger-data">
            <input
              type="text"
              placeholder="Full Name of Passenger2"
              required/>
            <input type="number"
                   placeholder="Age Passenger2"
                   required/>
          </div>
          <div className="secondary-passenger-data">
            <input
              type="text"
              placeholder="Full Name of Passenger3"
              required/>
            <input type="number"
                   placeholder="Age Passenger3"
                   required/>
          </div>
        </form>
      );
    }
    

    注意 const {value} = e.target;

    【讨论】:

      【解决方案4】:

      问题在于onChange 实现,它接受event 对象,而您尝试将事件设置为passengerData

      但是在使用setStateyou want to use functional updates 来避免过时状态时还有另一个问题。

      但是,在回调中使用e.target.event 将是have value closure on this value,因此您应该使用带有useRef 挂钩的引用:

      此解决方案不会在每次渲染时重新创建 onChange(因为 useCallbackuseState 的功能更新),也不会有任何陈旧状态。

      const passengerObj = {
        primary: {
          fullname: '',
          age: null
        },
        secondary: []
      };
      
      const App = () => {
        const [passengerdata, setPassengerData] = useState(passengerObj);
      
        const inputRef = useRef();
      
        const onChange = useCallback(() => {
          setPassengerData(prev => ({
            ...prev,
            primary: {
              ...prev.primary,
              fullname: inputRef.current.value
            }
          }));
        }, []);
      
        return (
          <input
            ref={inputRef}
            value={passengerdata.primary.fullname}
            onChange={onChange}
          />
        );
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-07-17
        • 2023-03-09
        • 1970-01-01
        • 2018-10-18
        • 2020-04-09
        • 2019-01-29
        • 1970-01-01
        相关资源
        最近更新 更多