使用Hook和标签更新React状态

时间:2019-09-30 03:06:15

标签: javascript reactjs jsx react-hooks

我对在两种情况下如何使用钩子更新React状态有语法疑问。

1)我有一个叫做company的州和一个填写它的表格。在联系部分中,有两个输入代表公司员工(姓名和电话号码)。但是,如果要联系的公司不止一个员工,则有一个“添加更多联系人”按钮,该按钮必须重复相同的输入(当然,目的是第二个联系人)。我怎样才能做到这一点?我的意思是,要在状态内的数组“联系人”中生成另一个索引,请在具有该数组的对象内增加totalOfContacts并创建输入标签,以便用户可以键入第二个联系人的数据?

2)当我键入任何输入时,代码将触发handleChange函数。 “名称”和“城市”已经更新了状态,因为它们是简单状态。但是,由于联系人姓名和他的电话号码是该州内部数组索引的一部分,我该如何更新呢?

下面的代码已经可以正常工作了,我的两个问题正好是两条注释行(第20和29行)。

“保存”按钮只需控制台即可。记录结果,以便我们对其进行监控。

现在谢谢。

enter image description here

import React, { useState, useEffect } from "react";

export default () => {
    const [company, setCompany] = useState({
        name: "", city: "",
        contact: {
            totalOfContact: 1,
            contacts: [
                {id: 0, contactName: "", telephoneNumber: ""}
            ]
        }
    })

    useEffect(() => {
        console.log("teste");
    })

    const handleChange = item => e => {
        if (item === "contactName" || "telephone") {
            // How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
        } else {
            setCompany({ ...company, [item]: e.target.value })
        }
    }

    const handleClick = (e) => {
        e.preventDefault();
        if (e.target.value === "add") {
            // How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
        } else {
            console.log(`The data of the company is: ${company}`);
        }
    }

    return (
        <div>
            <form>
                <h3>General Section</h3>
                Name: <input type="text" onChange = {handleChange("name")} value = {company.name} />
                <br />
                City: <input type="text" onChange = {handleChange("city")} value = {company.city} />
                <br />
                <hr />
                <h3>Contacts Section:</h3>
                Name: <input type="text" onChange = {handleChange("contactName")} value = {company.contact.contacts[0].name} />
                Telephone Numer: <input type="text" onChange = {handleChange("telephone")} value = {company.contact.contacts[0].telephoneNumber} />
                <br />
                <br />
                <button value = "add" onClick = {(e) => handleClick(e)} >Add More Contact</button>
                <br />
                <br />
                <hr />
                <button value = "save" onClick = {(e) => handleClick(e)} >Save</button>
            </form>
        </div>
    )
}

3 个答案:

答案 0 :(得分:2)

为回答您的问题,让我们将这个问题的范围缩小到一个更简单的问题,即如何处理联系人数组

您只需要了解以下内容:

  1. 地图功能
  2. 如何在不更改原始数组的情况下更新数组

我将使用TypeScript,以便您更好地理解。

const [state, setState] = React.useState<{
    contacts: {name: string}[]
}>({contacts: []})

return (
    <div>
        {state.contacts.map((contact, index) => {
            return (
                <div>
                    Name: 
                    <input value={contact.name} onChange={event => {
                      setState({
                          ...state,
                          contacts: state.contacts.map((contact$, index$) => 
                              index === index$
                                 ? {...contact$, name: event.target.value}
                                 : {...contact$}

                          )
                      })
                    }}/>
                </div>
            )
        }}
    </div>
)

此外,这种问题在React中相当普遍,因此了解并记住这种模式将对您有很大帮助。

答案 1 :(得分:2)

要更新状态值,可以使用functional setState

const handleChange = item => e => {
    //Take the value in a variable for future use
    const value = e.target.value;
    if (item === "contactName" || "telephone") {
        setCompany(prevState => ({
          ...prevState,
          contact: {...prevState.contact, contacts: prevState.contact.contacts.map(c => ({...c, [item]: value}))}
        }))
    } else {
        setCompany({ ...company, [item]: e.target.value })
    }
}

要在单击按钮时添加新的输入集,您可以这样做,

const handleClick = (e) => {
    e.preventDefault();
    //This is new set of input to be added
    const newSetOfInput = {id: company.contact.contacts.length, contactName: "", telephoneNumber: ""}
    if (e.target.value === "add") {
        // How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
        setCompany(prevState => ({
          ...prevState,
          contact: {...prevState.contact, contacts: prevState.contact.contacts.concat(newSetOfInput), totalOfContact: prevState.contact.contacts.length + 1}
        }))
    } else {
        console.log(`The data of the company is: ${company}`);
    }
}

最后,您需要遍历contacts数组,如

{company.contact.contacts && company.contact.contacts.length > 0 && company.contact.contacts.map(contact => (
    <div key={contact.id}>
    Name: <input type="text" onChange = {handleChange("contactName")} value = {contact.contactName} />
    <br/>
    Telephone Numer: <input type="text" onChange = {handleChange("telephoneNumber")} value = {contact.telephoneNumber} />
    </div>
))}

Demo

注意:您应该使用div之类的块元素,而不要使用<br/>

换行

答案 2 :(得分:1)

您可以执行以下操作。

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";


  const App = () => {
    const [company, setCompany] = useState({
      name: "",
      city: "",
      contact: {
        totalOfContact: 1,
        contacts: [{id: 0, contactName: "", telephoneNumber: ""}]
      }
    });

    console.log(company);

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

    const handleChange = (item, e,index) => {
      if (item === "contactName" || item === "telephoneNumber") {
        const contactsNew = [...company.contact.contacts];
        contactsNew[index] = { ...contactsNew[index], [item]: e.target.value };
        setCompany({
          ...company,
          contact: { ...company.contact, contacts: contactsNew }
        });
        // How can I set company.contact.contacts[<current_index>].contactName/telephoneNumber with the data typed?
      } else {
        setCompany({ ...company, [item]: e.target.value });
      }
    };

    const handleClick = e => {
      e.preventDefault();
      if (e.target.value === "add") {
        const contactNew = {...company.contact};
        contactNew.totalOfContact = contactNew.totalOfContact + 1;
        contactNew.contacts.push({id:contactNew.totalOfContact -1, contactName: "", telephoneNumber: ""});
        setCompany({...company, contact: {...contactNew}});
        // How can I set company.contact.totalOfContact to 2 and create one more set of inputs tags for a second contact?
      } else {
        alert("Push company to somewhere to persist");
        console.log(`The data of the company is: ${company}`);
      }
    };

    return (
      <div>
        <form>
          <h3>General Section</h3>
          Name:{" "}
          <input
            type="text"
            onChange={(e) => handleChange("name", e)}
            value={company.name}
          />
          <br />
          City:{" "}
          <input
            type="text"
            onChange={(e) => handleChange("city", e)}
            value={company.city}
          />
          <br />
          <hr />
          <h3>Contacts Section:</h3>
          {company.contact.contacts.map((eachContact, index) => {
            return <React.Fragment>
                Name:{" "}
                <input
                  type="text"
                  onChange={(e) => handleChange("contactName",e, index)}
                  value={eachContact.name}
                />
                Telephone Numer:{" "}
                <input
                  type="text"
                  onChange={(e) => handleChange("telephoneNumber",e, index)}
                  value={eachContact.telephoneNumber}
                />
              <br />
            </React.Fragment>
                })}

          
          <br />
          <button value="add" onClick={e => handleClick(e)}>
            Add More Contact
          </button>
          <br />
          <br />
          <hr />
          <button value="save" onClick={e => handleClick(e)}>
            Save
          </button>
        </form>
      </div>
    );
  };


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

您的状态结构看起来像是useReducer钩子的理想选择。我建议您尝试使用代替useState的方法。我想,您的代码应该以这种方式看起来很可读。 https://reactjs.org/docs/hooks-reference.html#usereducer