简体   繁体   English

使用 Hooks 和标签更新 React state

[英]Updating React state with Hooks and tags

I'm having a syntax doubt on how to update React state using hooks in 2 situations.我对如何在 2 种情况下使用钩子更新 React state 有语法疑问。

1) I have a state called company and a form that fills it up. 1)我有一个名为公司的 state 和一个填写它的表格。 In contact section, there are two inputs referring to the company employee (name and telephone number).在联系部分,有两个输入是指公司员工(姓名和电话号码)。 But if the company has more than one employee to be contacted, there is an "Add More Contact" button, which must duplicate the same inputs (of course, aiming to a second contact).但是,如果公司有多个要联系的员工,则有一个“添加更多联系人”按钮,该按钮必须复制相同的输入(当然,针对第二个联系人)。 How can I do that?我怎样才能做到这一点? I mean, to generate another index in the array "contacts" inside the state, increment the totalOfContacts inside the object that has that array and create the input tags so user can type the second contact's data?我的意思是,要在 state 内的数组“联系人”中生成另一个索引,增加具有该数组的 object 内的 totalOfContacts 并创建输入标签,以便用户可以键入第二个联系人的数据?

2) When I type any inputs, the code triggers the handleChange function. 2)当我输入任何输入时,代码会触发 handleChange function。 The "name" and "city" already update the state because they are simple states. “名称”和“城市”已经更新了 state,因为它们是简单的状态。 But how can I update the contact name and his telephone number, since they are part of an index of an array inside the state?但是我如何更新联系人姓名和他的电话号码,因为它们是 state 内数组索引的一部分?

The code below is already working and my 2 questions are exactly the two commented lines (lines 20 and 29).下面的代码已经在运行,我的 2 个问题正是注释的两行(第 20 行和第 29 行)。

The "Save" button simply console.log the results so we can monitor them. “保存”按钮只是简单地控制台记录结果,以便我们可以监控它们。

Thanks for now.现在谢谢。

在此处输入图像描述

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>
    )
}

To answer your question let us scope down this problem to a much simpler problem, which is how to handle array of contacts .为了回答你的问题,让我们把这个问题简化为一个更简单的问题,即如何处理联系人数组

You just need know the following things:你只需要知道以下几点:

  1. Map function Map function
  2. How to update array如何更新数组

I'll use TypeScript so you can understand better.我将使用 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>
)

Also, this kind of problem is fairly common in React, so understand and memorize this pattern will help you a lot.此外,这种问题在 React 中相当普遍,因此理解和记住这种模式会对你有很大帮助。

To update the state value, you can use functional setState ,要更新 state 值,您可以使用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 })
    }
}

To add new set of input on the click of button you can do this,要在单击按钮时添加新的输入集,您可以这样做,

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}`);
    }
}

Finally you need to iterate over your contacts array like,最后,您需要遍历您的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演示

Note: You should use block elements like div instead of breaking the line using <br/>注意:你应该使用像div这样的块元素,而不是使用<br/>

You can do something like this.你可以做这样的事情。

 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);

Your state structure looks like an ideal candidate for useReducer hook.您的 state 结构看起来是 useReducer 挂钩的理想候选者。 I would suggest you try that instead of useState.我建议你试试这个,而不是 useState。 Your code should look muck readable that way, I suppose.我想你的代码应该看起来很可读。 https://reactjs.org/docs/hooks-reference.html#usereducer https://reactjs.org/docs/hooks-reference.html#usereducer

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM