React:在w /事件中为setState传递一个Prop

时间:2019-09-07 04:58:35

标签: json reactjs events nested state

我正在使用通过onChange函数更新的嵌套状态对象,如下所示:

  const [someState, setSomeState] = useState({
    customer: [
      {
        name: "Bob",
        address: "1234 Main Street",
        email: "bob@mail.com",
        phone: [
          {
            mobile: "555-5555",
            home: "555-5555"
          }
        ]
      }
    ]
  });

  const updateSomeStatePhone = e => {
    e.persist();
    setSomeState(prevState => {
      prevState.customer[0].phone[0].mobile = e.target.value;
      return {
        ...prevState
      };
    });
  };

 <p>Update Mobile Number<p>
  <select
   value={someState.customer[0].phone[0].mobile}
   onChange={updateSomeStatePhone}
  >
   <option value="123-4567">"123-4567"</option>
  </select>

这完成了把戏。但是,目前,如果我想通过带有下拉菜单/输入字段等的大形式来更新多个状态属性,则必须为这些字段硬编码6个不同的onChange处理程序。

相反,我宁愿只使用一个onChange处理程序,并从form字段传递要更改的state属性的状态,但是我无法弄清楚语法:


  const updateSomeState = (e, prop) => {
    e.persist();
    setSomeState(prevState => {
      prevState.prop = e.target.value;
      return {
        ...prevState
      };
    });
  };

 <p>Update Mobile Number<p>
  <select
   value={someState.customer[0].phone[0].mobile}
   onChange={updateSomeState(e, prop)}
  >
   <option value="123-4567">"123-4567"</option>
  </select>

我尝试使用不同类型的语法将传入的“ prop”值链接到prevState:

prevState.prop = e.target.value;

prevState.(prop) = e.target.value;

${prevState} + '.' + ${prop} = e.target.value; // Dumb, I know

但是该函数永远无法识别我从该函数传入的“ prop”。我敢肯定必须有一个简单的方法来做到这一点。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:1)

是否必须是单个useState钩子?我建议使用useReducer或使用多个useState钩子对其进行简化。

多个useState钩子

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [name, setName] = React.useState("");
  const [address, setAddress] = React.useState("");
  const [email, setEmail] = React.useState("");
  const [mobile, setMobile] = React.useState("");
  const [home, setHome] = React.useState("");

  const getResult = () => ({
    customer: [
      {
        name,
        address,
        email,
        phone: [
          {
            mobile,
            home
          }
        ]
      }
    ]
  });

  // Do whatever you need to do with this
  console.log(getResult());

  return (
    <>
      <input
        value={name}
        placeholder="name"
        onChange={e => setName(e.target.value)}
      />
      <br />
      <input
        value={address}
        placeholder="address"
        onChange={e => setAddress(e.target.value)}
      />
      <br />
      <input
        value={email}
        placeholder="email"
        onChange={e => setEmail(e.target.value)}
      />
      <br />
      <input
        value={mobile}
        placeholder="mobile"
        onChange={e => setMobile(e.target.value)}
      />
      <br />
      <input
        value={home}
        placeholder="home"
        onChange={e => setHome(e.target.value)}
      />
    </>
  );
}

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

Edit form-with-multiple-hooks

单个useReducer(具有简化状态)

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

const reducer = (state, action) => {
  const { type, value } = action;

  switch (type) {
    case "SET_NAME":
      return { ...state, name: value };
    case "SET_ADDRESS":
      return { ...state, address: value };
    case "SET_EMAIL":
      return { ...state, email: value };
    case "SET_MOBILE":
      return { ...state, phone: [{ ...state.phone[0], mobile: value }] };
    case "SET_HOME":
      return { ...state, phone: [{ ...state.phone[0], home: value }] };
    default:
      throw Error(`Unexpected action: ${action.type}`);
  }
};

const initialState = {
  name: "",
  address: "",
  email: "",
  phone: [
    {
      mobile: "",
      home: ""
    }
  ]
};

function App() {
  const [state, dispatch] = React.useReducer(reducer, initialState);

  // Do what you need with state
  console.log(state);

  return (
    <>
      <input
        value={state.name}
        placeholder="name"
        onChange={({ target: { value } }) =>
          dispatch({ type: "SET_NAME", value })
        }
      />
      <br />
      <input
        value={state.address}
        placeholder="address"
        onChange={({ target: { value } }) =>
          dispatch({ type: "SET_ADDRESS", value })
        }
      />
      <br />
      <input
        value={state.email}
        placeholder="email"
        onChange={({ target: { value } }) =>
          dispatch({ type: "SET_EMAIL", value })
        }
      />
      <br />
      <input
        value={state.phone.mobile}
        placeholder="mobile"
        onChange={({ target: { value } }) =>
          dispatch({ type: "SET_MOBILE", value })
        }
      />
      <br />
      <input
        value={state.phone.home}
        placeholder="home"
        onChange={({ target: { value } }) =>
          dispatch({ type: "SET_HOME", value })
        }
      />
    </>
  );
}

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

Edit form-with-reducer

答案 1 :(得分:1)

useReducer是执行此操作的更好选择。互联网上的示例。

为什么不应该使用useState传递对象是因为它的行为不像setState。底层对象引用是相同的。因此,反应永远不会触发状态更改。如果要对对象使用相同的useState。您可能必须实现自己的版本以扩展该版本(下面的示例),或者可以直接使用useReducer挂钩来实现相同的目的。

下面是一个useState的示例,您可以在每次更改时注意到状态更新。

const [form, setValues] = useState({
    username: "",
    password: ""
});

const updateField = e => {
    setValues({
      ...form,
      [e.target.name]: e.target.value
    });
};

请注意其中的...form。您可以在每个所需的更新中执行此操作,也可以使用自己的实用程序或我提到的useReducer

现在进入您的代码,还有其他问题。

  1. 您正在将phone用作可以作为对象的数组。或者更好的是单独的属性也可以。无害。

  2. 如果将customers作为数组,则必须遍历记录。不只是通过硬编码来更新索引。如果只有一个客户,最好不要保留数组,而只保留一个对象。假设它是一系列客户,并且您正在遍历整个客户,那么这就是更新mobile的方法。

const updatedCustomers =  state.customers.map(item => {

   const { phone } = item;
   return { ...item, phone: { mobile: e.target.value }}; 
   // returns newCustomer object with updated mobile property
});

// Then go ahead and call `setSomeState ` from `useState`
setSomeState(...someState, { customer: updatedCustomers });// newState in your case is 
  

相反,我希望只使用一个onChange处理程序,然后传入   来自状态属性的表单字段中的状态   变化,但我不知道语法

如果您还没有从第一个示例中弄清楚这一点。这是简短的步骤。

  1. 给您的HTML元素一个name属性。
  2. 然后改为使用[e.target.name]
   return { ...item, phone: { [e.target.name]: e.target.value }}; 

答案 2 :(得分:0)

使用lodash的_.set助手。

const updateSomeState = (e, prop) => {
    e.persist();
    setSomeState(prevState => {
      let customers = [...prevState.customers] // make a copy of array
      let customer = {...customers[0]} // make a copy of customer object
      _.set(customer, prop, e.target.value)
      customers[0] = customer;
      return {
        ...prevState, customers 
      };
    });
  };

顺便说一句,在您现有的updateSomeStatePhone中,您正在修改应该是不可变的prevState对象。