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 σε διαφορετικό αρχείο.