Sådan opbygges en Todo-liste med React Hooks

Reager v16.7.0-alpha introducerede kroge, og jeg er begejstret.

Hvad er kroge?

Det er funktioner, der giver dig React-funktioner som tilstands- og livscyklusknager uden ES6-klasser.

Nogle fordele er

  • Isolering af stateful logik, hvilket gør det lettere at teste.
  • Deling af stateful logik uden render rekvisitter eller komponenter af højere orden.
  • Adskille din apps bekymringer baseret på logik, ikke livscykluskrog.
  • Undgå ES6-klasser, fordi de er pæne, ikke faktisk klasser, og snubler endda erfarne JavaScript-udviklere.

For flere detaljer se React's officielle Hooks intro.

Adopter kroge gradvist

I skrivende stund var Hooks i alfa, og deres API kunne have ændret sig når som helst.

React 16.8.0 var den første stabile udgivelse, der understøttede Hooks, og der er flere tutorials og eksempelkoder hver dag. Da der imidlertid ikke er planer om at fjerne klasser fra React, og Hooks fungerer med eksisterende kode, anbefaler React-teamet at undgå "store omskrivninger". I stedet for foreslår de at øve kroge i ikke-kritiske komponenter først og derefter bruge dem i stedet for klasser fremover.

Lad os opbygge en Todo-liste

Todo-lister er det mest overudnyttede eksempel af en god grund - de er fantastisk praksis. Jeg anbefaler dette til ethvert sprog eller bibliotek, du vil prøve.

Vores vil kun gøre et par ting

  • Vis todos på en flot Material Design-måde
  • Tillad tilføjelse af todos via input
  • Slet todos

Opsætning

Her er GitHub- og CodeSandbox-linkene.

git clone //github.com/yazeedb/react-hooks-todo cd react-hooks-todo npm install 

Den mastergren har det færdige projekt, så kassen startgren, hvis du ønsker at følge med.

git checkout start

Og kør projektet.

npm start

Appen skal køre localhost:3000, og her er vores første brugergrænseflade.

Det er allerede oprettet med materiale-ui for at give vores side et professionelt udseende. Lad os begynde at tilføje nogle funktioner!

TodoForm-komponenten

Tilføj en ny fil src/TodoForm.js. Her er startkoden.

import React from 'react'; import TextField from '@material-ui/core/TextField'; const TodoForm = ({ saveTodo }) => { return (    ); }; export default TodoForm; 

I betragtning af navnet ved vi, at dets opgave er at tilføje todos til vores stat. Når vi taler om, her er vores første krog .

useState

Tjek denne kode ud

import { useState } from 'react'; const [value, setValue] = useState(''); 

useStateer bare en funktion, der tager starttilstand og returnerer en matrix. Gå videre og console.logdet.

Arrayets første indeks er din stats aktuelle værdi, og det andet indeks er en opdateringsfunktion.

Så vi navngav dem passende valueog setValuebrugte ES6-destruktureringsopgave.

useState med formularer

Vores formular skal spore inputets værdi og kalde saveTodopå send. useStatekan hjælpe os med det!

Opdatering TodoForm.js, den nye kode er med fed skrift .

import React, { useState } from 'react'; import TextField from '@material-ui/core/TextField'; const TodoForm = ({ saveTodo }) => { const [value, setValue] = useState(''); return (  { event.preventDefault(); saveTodo(value); }} >  { setValue(event.target.value); }} value={value} />  ); }; export default TodoForm; 

Tilbage i index.js, importer og brug denne komponent.

// ... import TodoForm from './TodoForm'; // ... const App = () => { return ( Todos ); }; 

Nu er din værdi logget på send (tryk på enter).

useState With Todos

Vi har også brug for tilstand for vores todos. Import useStatei index.js. Vores oprindelige tilstand skal være en tom matrix.

import React, { useState } from 'react'; // ... const App = () => { const [todos, setTodos] = useState([]); // ... }; 

TodoList-komponent

Opret en ny fil kaldet src/TodoList.js.

Rediger: Tak Takahiro Hata for at hjælpe mig med at flytte onClicktil det rigtige sted!

import React from 'react'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; import ListItemText from '@material-ui/core/ListItemText'; import Checkbox from '@material-ui/core/Checkbox'; import IconButton from '@material-ui/core/IconButton'; import DeleteIcon from '@material-ui/icons/Delete'; const TodoList = ({ todos, deleteTodo }) => (  {todos.map((todo, index) => (      { deleteTodo(index); }} >     ))}  ); export default TodoList; 

Det tager to rekvisitter

  • todos: Matrixen af ​​todos. Vi mapover hver enkelt og opretter et listeelement.
  • deleteTodo: Clicking a todo’s IconButton fires this function. It passes the index, which will uniquely identify a todo in our list.

Import this component in your index.js.

import TodoList from './TodoList'; import './styles.css'; const App = () => { //... }; 

And use it in your App function like so

Adding Todos

Still in index.js, let’s edit our TodoForm’s prop, saveTodo.

 { const trimmedText = todoText.trim(); if (trimmedText.length > 0) { setTodos([...todos, trimmedText]); } }} />

Simply merge the existing todos with our new one, extra whitespace cut out.

We can add todos now!

Clearing the Input

Notice the input isn’t clearing after adding a new todo. That’s a bad user experience!

We can fix it with a small code change in TodoForm.js.

 { event.preventDefault(); saveTodo(value); setValue(''); }} />

Once a todo’s saved, set the form state to an empty string.

It’s looking good now!

Deleting Todos

TodoList provides each todo’s index, as it’s a guaranteed way to find the one we want to delete.

TodoList.js

 { deleteTodo(index); }} >

Vi drager fordel af det i index.js.

 { const newTodos = todos.filter((_, index) => index !== todoIndex); setTodos(newTodos); }} />

Uanset hvilke todos der ikke matcher det leverede index, opbevares og opbevares de i tilstand ved hjælp af setTodos.

Slet funktionalitet er fuldført!

Abstraktion af Todos useState

Jeg nævnte, at kroge er gode til at adskille tilstands- og komponentlogik. Her er hvordan det kan se ud i vores todo-app.

Opret en ny fil kaldet src/useTodoState.js.

import { useState } from 'react'; export default (initialValue) => { const [todos, setTodos] = useState(initialValue); return { todos, addTodo: (todoText) => { setTodos([...todos, todoText]); }, deleteTodo: (todoIndex) => { const newTodos = todos.filter((_, index) => index !== todoIndex); setTodos(newTodos); } }; }; 

Det er vores samme kode fra index.js, men adskilt! Vores statsadministration er ikke længere tæt knyttet til komponenten.

Nu skal du bare importere det.

import React from 'react'; import ReactDOM from 'react-dom'; import Typography from '@material-ui/core/Typography'; import TodoForm from './TodoForm'; import TodoList from './TodoList'; import useTodoState from './useTodoState'; import './styles.css'; const App = () => { const { todos, addTodo, deleteTodo } = useTodoState([]); return ( Todos   { const trimmedText = todoText.trim(); if (trimmedText.length > 0) { addTodo(trimmedText); } }} /> ); }; const rootElement = document.getElementById('root'); ReactDOM.render(, rootElement); 

Og alt fungerer stadig som normalt.

Abstraktion af formularinput useState

Vi kan gøre det samme med vores form!

Opret en ny fil, src/useInputState.js.

import { useState } from 'react'; export default (initialValue) => { const [value, setValue] = useState(initialValue); return { value, onChange: (event) => { setValue(event.target.value); }, reset: () => setValue('') }; }; 

Og nu TodoForm.jsskal se sådan ud.

import React from 'react'; import TextField from '@material-ui/core/TextField'; import useInputState from './useInputState'; const TodoForm = ({ saveTodo }) => { const { value, reset, onChange } = useInputState(''); return (  { event.preventDefault(); saveTodo(value); reset(); }} >   ); }; export default TodoForm; 

Og vi er alle færdige! Håber du nød, indtil næste gang!