Lekce 4. - React - Formuláře I.

05.03.2021 Programování #react #programování

Pokračování ve výuce Reactu, tentokrát se zaměřením na formuláře.


Elementy formátu HTML pracují trochu jinak než jiné prvky DOM v Reactu, protože elementy formulářů přirozeně udržují nějaký vnitřní stav. Například tento formulář ve formátu HTML přijímá jediné jméno:

<form>
  <label>
    Name:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="Submit" />
</form>

Tento formulář má výchozí chování formuláře HTML. V Reactu ve většině případů je vhodné mít funkci JavaScriptu, která zpracovává odeslání formuláře a má přístup k údajům, které uživatel zadal do formuláře. Standardní způsob, jak toho dosáhnout, je technika s názvem "řízené komponenty".

Přidávání formulářů v React

V Reactu se přidávají formuláře stejně, jako každý jiný prvek:

Přidání zaškrtávacích políček k položkám Todo

V Reactu funguje zpracování formulářů trochu jinak.

Otevřete TodoItem.js soubor a přidejte zaškrtávací políčko input těsně před title li prvku.

return (
  <li>
    <input type="checkbox" /> {this.props.todo.title}
  </li>
)

Uložte soubor a podívejte se na zaškrtávací políčka v rozhraní frontend.

Ve výchozím nastavení je typ input checkbox (tj. Zaškrtávací políčka) zpracováván DOM - tj. Mají výchozí chování HTML. Proto můžete políčka přepínat.

Tento typ vstupu se nazývá nekontrolovaný vstup. To, ale není případ Reactu. Vstupní pole jsou určena k ovládání.

Řízená komponenta

Aby bylo možné vstupní pole ovládat, musí se se vstupními daty (v tomto případě přepínáním zaškrtávacího políčka) manipulovat stav komponenty a nikoli DOM prohlížeče. Díky tomu bude stav sloužit jako jediný zdroj dat pro zaškrtnutí.

To znamená, že zaškrtávací políčko vstupu již nebude poslouchat jeho vnitřní stav (tj. DOM prohlížeče), ale stav ve vaší aplikaci. To je nutné, protože stav komponenty se nezmění, pokud jej nezměníte.

Podívejme se, jak to funguje. Pokud se podíváte na objekt state v nadřazené komponentě, tak vidíme, že máme ke každému klíči v datech přiřazenou logickou hodnotu ( true nebo false). Můžeme na políčko kliknout a přepnout zaškrtávací tak políčko. completed todos

Jděte tedy do souboru TodoItem.js a přidejte atribut checked. Poté přiřaďte booleovskou hodnotu z objektu state.

Nezapomeňte, že k těmto hodnotám můžeme přistupovat stejným způsobem, jako jsme přistupovali k title. Zaškrtávací políčko by mělo vypadat takto:

<input type="checkbox" checked={this.props.todo.completed} />

Uložte soubor.

V tomto okamžiku jste uspěli při vytváření vstupu zaškrtávacího políčka jako kontrolovaného vstupu, protože nyní poslouchá pouze stav ve vaší aplikaci.

Pokud se nyní pokusíte přepnout kterékoli ze zaškrtávacích políček, nic se nestane. Je tomu tak proto, že každému z atributů checked je přiřazena hodnota rovnající se aktuální hodnotě stavu.

Pamatujte, že k dokončení je přiřazen pouze první úkol. Potřebujeme způsob, jak změnit stav, kdykoli uživatelé kliknou na zaškrtávací políčka. React nám již dává nápovědu prostřednictvím karty Console v prohlížeči DevTools.

Pokud jej otevřete, zobrazí se varování v důsledku přidaného atributu checked. React nám říká, abychom přidali obslužný program onChange, který bude sledovat všechny změny v poli. Jinak by nevěděl, zda je vstupní pole zaškrtnuto nebo ne.

Aktualizujme tedy vstupní značku v souboru TodoItem.js tak, aby zahrnovala obslužnou rutinu.

<input
  type="checkbox"
  checked={this.props.todo.completed}
  onChange={() => console.log("clicked")}
/>

Uložte soubor a znovu se podívejte na kartu Konzola, uvidíte, že varování zmizelo.

Mezitím přiřazujeme obslužné rutině funkci zpětného volání, která při každém kliknutí na zaškrtávací políčko zaznamená konzolový text v konzole.

Otevřete konzolu a zkuste kliknout na zaškrtávací políčka. Nyní namísto protokolování textu v konzole chceme přepínat zaškrtávací políčka, kdykoli na ně kliknete. Abychom to mohli udělat, musíme pochopit, jak vyvolávat a zpracovávat události.

Manipulace s formuláři

Zpracování formulářů je o tom, jak zacházíte s daty, když mění hodnotu nebo jsou odeslány. V HTML jsou data formulářů obvykle zpracovávána DOMem. V Reactu s daty formuláře obvykle manipulují komponenty. Když data zpracovávají komponenty, všechna data se uloží do komponenty state. Změny můžete ovládat přidáním obslužných rutin událostí do atributu onChange:

Příklad:

Podmíněné vykreslování

Pokud nechcete zobrazovat prvek h1, dokud uživatel neprovede žádný vstup, můžete přidat příkaz if.

Podívejte se na níže uvedený příklad a všimněte si následujícího:

1. Vytvoříme prázdnou proměnnou, v tomto příkladu ji nazýváme header.

2. Přidáme příkaz if pro vložení obsahu do proměnné header, pokud uživatel provedl jakýkoli vstup.

3. Proměnnou header vložíme do výstupu pomocí složených závorek.

Odesílání formulářů

Akce odeslání můžete ovládat přidáním obslužné rutiny události do atributu onSubmit:

Více vstupních polí

Hodnoty více než jednoho vstupního pole můžete ovládat přidáním atributu name ke každému prvku.

Když inicializujete stav v konstruktoru, použijte názvy polí.

Pro přístup k polím v obslužné rutině události použijte syntaxi event.target.name event.target.value.

Chcete-li aktualizovat stav v this.setState metodě, použijte hranaté závorky [notace závorky] kolem názvu vlastnosti.

Zpět k aplikaci ToDo

V naší aplikaci je nadřazená komponenta TodoContainer, která obsahuje údaje o stavu. Tato součást je tedy JEN ta, která ji může změnit. Což znamená, že položka TodoItem, která obsahuje zaškrtávací políčka, nemůže měnit stav dat v nadřazené složce TodoContainer.

Musíme najít způsob, jak přistupovat ke stavovým datům z TodoItem a přepnout hocnotu completed na true nebo false v komponentě TodoContainer.

K tomu budeme muset událost pozvednout z TodoItem vyšší úrovně na TodosList a poté do koméonenty TodoContainer. Jinými slovy, musíme vylézt po žebříku.

handlingevent_

Položka TodoItem zvýší událost, zatímco mateřská komponenta TodoContainer bude zpracovávat událost. A způsob, jakým to uděláme, je skrz props.

Nejprve povolíme komunikaci mezi těmito komponentami. Do nadřazené komponenty TodoContainer přidejte metodu obslužné rutiny handleChange těsně nad matodu render().

handleChange = () => {
  console.log("clicked");
};

Tuto metodu můžete pojmenovat, jak chcete. Podívejme se, jak můžeme s touto metodou komunikovat z podřízené komponenty.

Začněte předáním této metody TodosList komponentě prostřednictvím vlastností (props). Aktualizujte < TodosList / >, abyste měli:

<TodosList todos={this.state.todos} handleChangeProps={this.handleChange} />

Poznámka: Používáme this.handleChangek odkazování na handleChange()metodu, protože je součástí třídy.

Nyní máte metodu handleChange() přiřazenou k handleChangeProps. K datům lze přistupovat prostřednictvím vlastností v komponentě TodosList.

Odtud jej můžeme předat komponentě TodoItem. Aktualizujme instanci < TodoItem / > v souboru TodosList.js, abychom měli:

<TodoItem
  key={todo.id}
  todo={todo}
  handleChangeProps={this.props.handleChangeProps}
/>

V tomto okamžiku lze k datům handleChange() přistupovat z komponenty TodoItem. Aktualizujte tedy obslužnou rutinu onChange v komponentě TodoItem, abychom měli:

onChange={() => this.props.handleChangeProps()}

Tentokrát se ujistěte, že máte připojené k handleChangeProps závorky (). Uložte všechny své soubory. Teď, když klepnete na některý z políček se událost onChange spustí a bude volat metodu handleChange() v nadřazené složce TodoContainer.

Prozatím pouze protokolujeme text v konzole.

Nyní musíme určit, na které ze zaškrtávacích políček je kliknuto. K tomu musíme projít jejich příslušnou ids funkcí zpětného volání.

Aktualizujte popisovač onChange v komponentě TodoItem tak, aby zahrnoval id.

onChange={() => this.props.handleChangeProps(this.props.todo.id)}

Nezapomeňte, že stejně jako hodnoty title completed, máme také přístup k id v této komponentě. Uložte soubor. Pak přejděte do komponenty TodoContainer a aktualizujte metodu handleChange.

handleChange = (id) => {
  console.log("clicked", id);
};

Všimněte si, jak přijímáme a přihlašujeme id. Uložte soubor. Otevřete konzolu a klikněte na zaškrtávací políčka. Tentokrát uvidíte jejich příslušná ID.

handlingclick

Aktualizace stavu pomocí metody setState()

Naším cílem je zde změnit stav zaškrtávacího políčka, kdykoli na něj kliknete. V React neměníme stav přímo. Místo toho aktualizujeme pomocí metody, kterou jsme zdědili rozšířením React.Component.

Tato metoda se nazývá setState(). Říká React, že aktualizujeme stav. React by pak naplánoval aktualizace místního stavu komponenty a zjistil, která část stavu se změnila. Poté aktualizuje POUZE tuto část ve skutečném DOM.

V tuto chvíli, pokud klikneme na kterékoli ze zaškrtávacích políček, dostáváme text a id handleChange. Nyní pro každý přepínač komponenta TodoContainer naplánuje aktualizaci uživatelského rozhraní voláním setState() s objektem obsahujícím aktuální úlohy. Nezapomeňte, že aktuální todos získáme procházením dat todos a zkontrolujeme, zda některá z položek id odpovídá zaškrtnutému id. Pokud ano, změníme dokončenou hodnotu z true na false a naopak.

Zde je konkrétní kód:

handleChange = id => {
  this.setState({
    todos: this.state.todos.map(todo => {
      if (todo.id === id) {
        todo.completed = !todo.completed;
      }
      return todo;
    })
  });
};

Nezapomeňte aktualizovat metodu handleChange metodu v komonentě TodoContainer na výše uvedený kód.

React nyní prostřednictvím voláním setState() ví, že se stav změnil. Potom znovu zavolá metodu render(), aby znovu vykreslila a podle toho aktualizovala obrazovku.

Uložte soubor a zkontrolujte svou aplikaci. Měli byste být schopni přepínat zaškrtávací políčka.

stateupdate

Aktuální stav vaší aplikace můžete zobrazit také v React Tools.

Přidání aktualizátoru do setState

Již víte, že metoda setState() se v komponentě třídy používá k aktualizaci stavu. Využití v handleChange() lze však dále optimalizovat. V naší aplikaci, přepínáme zaškrtávací políčko. A to může být true nebo false.

V tomto případě je nový stav vypočítán na základě předchozí hodnoty. Můžete také najít jiný scénář, kdykoli aktualizujete hodnotu čítače nebo kliknete na tlačítko.

Takže když nastane tento případ, můžete předat funkci aktualizátoru do setState. Funkce obdrží předchozí verzi stavu a vrátí aktualizovanou hodnotu.

this.setState(prevState => ({
  //update state
}))

Důvodem je to, že React nezaručuje, že this.state napsané v rámci setState() je aktuální. Aktualizace může být dávkována nebo odložena na později.

React tedy nedoporučuje se spoléhat na hodnotu pro výpočet dalšího stavu, protože by to mohlo vést k nezamýšlenému výstupu. Pokud nyní aktualizujete handeChange(), měli byste mít:

this.setState(prevState => ({
  todos: prevState.todos.map(todo => {
    if (todo.id === id) {
      todo.completed = !todo.completed
    }
    return todo
  }),
}))

V kódu nyní používáme snapshot ( prevState) v rámci setState() k získání předchozího stavu namísto použití this.state.

Uložte soubor a otestujte svou práci.

Nastal problém. Zaškrtávací políčka se nám již nepodařilo přepnout. Pokud dočasně odeberete < React.StrictMode > ze souboru src/index.js a uložíte jej, začnou znovu fungovat zaškrtávací políčka.

Protože chceme povolit strikltní režim, přidejte jej prosím zpět.

V souboru TodoContainer.js musíme dále optimalizovat metodu handleChange().

V tuto chvíli aktualizujeme kontrolovanou hodnotu ve stavu přímo prostřednictvím todo.complete = !todo.completed. Striktnímu režimu se to nelíbí.

Místo toho aktualizujme kód, abyste měli:

this.setState(prevState => ({
  todos: prevState.todos.map(todo => {
    if (todo.id === id) {
      return {
        ...todo,
        completed: !todo.completed,
      }
    }
    return todo
  }),
}))

Uložte soubor a otestujte svou práci. Měli byste být schopni znovu zaškrtávací políčka přepínat.

Všimněte si, jak se balí objekt v setState zpětného volání v závorkách (). Alternativou je použití příkazu return:

this.setState(prevState => {
  return {
    todos: prevState.todos.map(todo => {
      if (todo.id === id) {
        return {
          ...todo,
          completed: !todo.completed,
        }
      }
      return todo
    }),
  }
})

Obě metody fungují dobře.

Mazání položek úkolů

Bude to podobné tomu, jak jsme zpracovali vstupní zaškrtávací políčka. Zde také položky todos žijí v TodoContainer, zatímco tlačítko mazání bude aktivní v komponentě TodoItem.

To znamená, že budeme posouvat událost z komponenty TodoItem o úroveň výš, dokud se nedostaneme ke komponentě TodoContainer, kde bude událost zpracována.

Začněte přidáním tlačítka pro odstranění v komponentě TodoItem. Přidejte tedy prvek button pod značku input (ale před nadpis):

<button>Delete</button>

Mezitím místo správné ikony pro mazání napíšeme text „Delete“. Později v se naučíte, jak přidat ikony SVG do vaší aplikace React.

Jako obvykle umožníme komunikaci mezi těmito komponentami za účelem vyvolání události. Takže jděte do komponenty TodoContainer a přidejte metodu delTodo nad render().

delTodo = id => {
  console.log("deleted", id);
};

Dále aktualizujte, < TodosList / > aby zahrnovalo:

deleteTodoProps={this.delTodo}

Takže máme:

<TodosList
  todos={this.state.todos}
  handleChangeProps={this.handleChange}
  deleteTodoProps={this.delTodo}
/>

Uložte soubor a přesuňte se o úroveň níže do komponenty TodosList a aktualizujte < TodoItem / >, abyste měli:

<TodoItem
  key={todo.id}
  todo={todo}
  handleChangeProps={this.props.handleChangeProps}
  deleteTodoProps={this.props.deleteTodoProps}
/>

Nakonec přejděte do komponenty TodoItem. Aktualizujte prvek button tak, aby zahrnoval obslužnou rutinu události onClick, která spustí metodu delTodo v nadřazené komponentě. Měli byste mít toto:

<button onClick={() => this.props.deleteTodoProps(this.props.todo.id)}>
  Delete
</button>

Uložte všechny své soubory a zkontrolujte rozhraní. Otevřete kartu Konzola a zkuste odstranit všechny položky úkolů. V tuto chvíli provádíme logování „odstraněného“ textu vedle ID odstraněných položek.

Až do tohoto okamžiku opakujeme to, co jsme udělali pro zaškrtávací políčko. Pokud to není jasné, vraťte se k dřívějšímu vysvětlení.

Dále budeme manipulovat se stavem a odstraníme všechny odstraněné položky ze seznamu. Způsob, jakým to děláme, je pomocí metody filter(). Tato metoda je také funkcí vyššího řádu, stejně jako metoda map(). Vrátí nové pole uplatněním podmínky na každý prvek pole.

V tomto případě chceme vrátit pouze položky todos, které neodpovídají ID, které bude předáno - tj. kliknutí na ID. Jakékoli shodné ID bude smazáno. Nyní aktualizujte metodu delTodo, abyste měli:

delTodo = id => {
  this.setState({
    todos: [
      ...this.state.todos.filter(todo => {
        return todo.id !== id;
      })
    ]
  });
};

S touto metodou filter() říkáme, že pro každé z dat todos, které procházíme smyčkou, chceme zachovat pouze ty, jejíž id se nerovná id předávané.

Poznámka: operátor spread (  )  umožňuje spojovat aktuální položky todos.

Uložte soubor a otestujte aplikaci. Měli byste být schopni odstranit všechny položky ze seznamu.