useReducer()
Με την useReducer() αντικαθιστούμε την useState() και μεταφέρουμε τη λογική σε μια ξεχωριστή συνάρτηση που την ονομάζουμε reduser function.
Στο παρακάτω παράδειγμα η εφαρογή TodoList είναι φτιαγμένη με την useState().
import { useState } from 'react'; export default function TodoList() { const [todoListItems, setTodoListItems] = useState([]); function handleAddListItem() { let item = window.prompt("New Item"); if(item !== null && item !== "") { setTodoListItems([...todoListItems, item]); } } function handleDeleteListItem(index) { let counter = 0; setTodoListItems(todoListItems.filter((item) => counter++ !== index)); } function handleEditListItem(i) { let editedItem = window.prompt("Edit item", todoListItems[i]); if(editedItem !== null) { let temp = todoListItems.map((item, index) => { if(index === i) { return editedItem; } else { return item; } }); setTodoListItems(temp); } } return ( <> <h1>Todo List</h1> <button onClick = {handleAddListItem}>Add item</button> <ul>{ todoListItems.map((item, index) => <li key = {index}>{item} <button onClick = {() => handleDeleteListItem(index)} >delete</button> <button onClick = {() => handleEditListItem(index)} >edit</button> </li>) } </ul> </> ); }
Στο παρακάτω παράδειγμα η ίδια εφαρογή TodoList είναι φτιαγμένη με την useReducer().
import { useReducer } from 'react'; function todoListReducer(todoListItems, action) { switch (action.type) { case 'add': { return [...todoListItems, action.text]; } case 'edit': { return todoListItems.map((item, index) => { if(index === action.index) { return action.text; } else { return item; } }); } case 'delete': { let counter = 0; return todoListItems.filter((item) => counter++ !== action.index) } default: { throw Error('Unknown action: ' + action.type); } } } export default function TodoList() { const [todoListItems, dispatch] = useReducer(todoListReducer, []); function handleAddListItem() { let text = window.prompt("New Item"); if(text !== null && text !== "") { dispatch({ type: 'add', text: text, }); } } function handleDeleteListItem(index) { dispatch({ type: 'delete', index: index }); } function handleEditListItem(index) { let text = window.prompt("Edit item", todoListItems[index]); if(text !== null && text !== "") { dispatch({ type: 'edit', index: index, text: text }); } } return ( <> <h1>Todo List</h1> <button onClick = {handleAddListItem}>Add item</button> <ul>{ todoListItems.map((item, index) => <li key = {index}>{item} <button onClick = {() => handleDeleteListItem(index)} >delete</button> <button onClick = {() => handleEditListItem(index)} >edit</button> </li>) } </ul> </> ); }
Στο δεύτερο παράδειγμα όλες οι αλλαγές της state variable γίνονται σε μία ξεχωριστή συνάρτηση και έτσι έχουμε έναν καλύτερο έλεγχο.
Η useReducer παίρνει δύο παραμέτρους: τη συνάρτηση reducer (που στο παράδειγμά μας είναι η todoListReducer) και αρχική τιμή για την state variable (που στο παράδειγμά μας είναι ένας πίνακας).
Η reducer είναι μια pure συνάρτηση και δεν είναι component.
Εάν θέλετε, μπορείτε να μετακινήσετε τη συνάρτηση reducer σε διαφορετικό αρχείο.