Обновление состояния React с помощью хуков и тегов - PullRequest
0 голосов
/ 30 сентября 2019

У меня есть синтаксическое сомнение относительно того, как обновить состояние React с использованием хуков в 2 ситуациях.

1) У меня есть состояние, называемое компанией, и форма, которая его заполняет. В разделе контактов есть два входа, относящихся к сотруднику компании (имя и номер телефона). Но если с компанией нужно связаться более чем с одним сотрудником, есть кнопка «Добавить еще контакт», которая должна дублировать одни и те же входные данные (конечно, нацеливаясь на второй контакт). Как я могу это сделать? Я имею в виду, чтобы сгенерировать еще один индекс в массиве «contacts» внутри состояния, увеличить totalOfContacts внутри объекта, который имеет этот массив, и создать теги ввода, чтобы пользователь мог вводить данные второго контакта?

2) КогдаЯ набираю любые входы, код запускает функцию handleChange. «Имя» и «город» уже обновляют состояние, потому что это простые состояния. Но как я могу обновить имя контакта и его номер телефона, поскольку они являются частью индекса массива внутри штата?

Код ниже уже работает, и мои 2 вопроса - это точно две прокомментированные строки (строки 20 и 29).

Кнопка «Сохранить» просто console.log результатов, чтобы мы могли отслеживать их.

Спасибо за сейчас.

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 ]

2 голосов
/ 30 сентября 2019

Чтобы обновить значение состояния, вы можете использовать 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>
))}

Демо

Примечание: Вы должны использовать блочные элементы, такие как div вместо разрыва строки, используя <br/>

2 голосов
/ 30 сентября 2019

Чтобы ответить на ваш вопрос, давайте разберем эту проблему до гораздо более простой проблемы: как обращаться с массивом контактов .

Вам просто нужно знать следующее:

  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 голос
/ 30 сентября 2019

Вы можете сделать что-то вроде этого.

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

...