Skip to main content

React Native Reducer

Victoria Harbour, Hongkong

Github Repository

Reducer

In a previous step I created an React Native application that used the React Context API using Context and Hooks to manage a To-Do List. We can now use reducers to centralise all methods to change the state of our application in a single function.

To-Do Context Provider

Without Reducer

import React, { createContext, useState } from 'react'
import {v4 as uuidv4} from 'uuid'

export const TodoItemContext = createContext()

const TodoItemContextProvider = ( {children} ) => {
    const [items, setItems] = useState([
        { text: 'Have a great day!', id: `${uuidv4()}`}
    ])

    const addItem = (item) => {
        setItems([...items, {text: item, id: `${uuidv4()}`}])
    }

    const removeItem = (id) => {
        setItems(items.filter((item) => {
            return item.id !== id
        }))
    }

    return (
        <TodoItemContext.Provider value={{items, addItem, removeItem}}>
            {children}
        </TodoItemContext.Provider>
        )
}

export default TodoItemContextProvider

With Reducer

import React, { createContext, useReducer } from 'react'
import {v4 as uuidv4} from 'uuid'

export const TodoItemContext = createContext()

const todosReducer = (state, action) => {
    switch(action.type) {
        case 'ADD_TODO':
            return[...state, {text: action.text, id: `${uuidv4()}`}]

        case 'REMOVE_TODO':
            return state.filter((todo) => todo.id !== action.id)

        default:
            return state
    }
}

const TodoItemContextProvider = ( {children} ) => {
    const [todos, dispatch] = useReducer(todosReducer, [
        { text: 'Have a great day!', id: `${uuidv4()}`}
    ])

    return (
        <TodoItemContext.Provider value={{todos, dispatch}}>
            {children}
        </TodoItemContext.Provider>
        )
}

export default TodoItemContextProvider

To-Do Component

Without Reducer

import React, { useContext, useState } from 'react'
import { Text, TouchableOpacity } from 'react-native'

import { TodoBackground, TodoList, TodoItem, LoginView, InputItem } from './_styles'
import { ThemeContext } from '../context/ThemeContext'
import { AuthContext } from '../context/AuthContext'
import { TodoItemContext } from '../context/TodoItemContext'

const ToDoList = () => {

    const [todo, setTodo] = useState('')

    const { isDarkTheme, darkTheme, lightTheme } = useContext(ThemeContext)
    // if isDarkTheme is true return dark state / else light
    const theme = isDarkTheme ? darkTheme : lightTheme

    const { isLoggedIn, changeAuthStatus } = useContext(AuthContext)
    const { items, addItem, removeItem } = useContext(TodoItemContext)

    const handleChange = (text) => {
        setTodo(text)
    }

    const handleAddItem = () => {
        if(todo.length > 0) 
        addItem(todo)
        setTodo('')
    }

    const handleRemoveItem = (id) => {
        removeItem(id)
    }

    if (isLoggedIn) {
        return (
            <TodoBackground style={theme}>
                {
                    items.length ? (
                        <TodoList 
                            data={items}
                            keyExtractor={(item) => item.id}
                            showsVerticalScrollIndicator={false}
                            renderItem={({item}) => {
                                return <TouchableOpacity onPress={() => handleRemoveItem(item.id)}><TodoItem>{item.text}</TodoItem></TouchableOpacity>
                            }}
                        />
                    ) : (
                        <TodoItem>Nothing to do...</TodoItem>
                    )
                }
                <InputItem
                    value={todo}
                    onChangeText={(text) => handleChange(text)}
                />
                <TodoItem onPress={handleAddItem}>
                    <Text>Add Item</Text>
                </TodoItem>
                <TodoItem onPress={changeAuthStatus}>
                    <Text>Logout</Text>
                </TodoItem>
            </TodoBackground>
        )}
        return (
            <LoginView>
                <TodoItem onPress={changeAuthStatus}>
                    <Text>Login</Text>
                </TodoItem>
            </LoginView>
        )
}

export default ToDoList

With Reducer

import React, { useContext, useState } from 'react'
import { Text, TouchableOpacity } from 'react-native'

import { TodoBackground, TodoList, TodoItem, LoginView, InputItem } from './_styles'
import { ThemeContext } from '../context/ThemeContext'
import { AuthContext } from '../context/AuthContext'
import { TodoItemContext } from '../context/TodoItemContext'

const ToDoList = () => {

    const [todo, setTodo] = useState('')

    const { isDarkTheme, darkTheme, lightTheme } = useContext(ThemeContext)
    // if isDarkTheme is true return dark state / else light
    const theme = isDarkTheme ? darkTheme : lightTheme

    const { isLoggedIn, changeAuthStatus } = useContext(AuthContext)

    const { todos, dispatch } = useContext(TodoItemContext)

    const handleChange = (text) => {
        setTodo(text)
    }

    const handleAddItem = () => {
        if(todo.length > 0) 
        dispatch({type: 'ADD_TODO', text: todo})
        setTodo('')
    }

    const handleRemoveItem = (id) => {
        dispatch({type: 'REMOVE_TODO', id})
    }

    if (isLoggedIn) {
        return (
            <TodoBackground style={theme}>
                {
                    todos.length ? (
                        <TodoList 
                            data={todos}
                            keyExtractor={(todo) => todo.id}
                            showsVerticalScrollIndicator={false}
                            renderItem={({ item }) => {
                                return <TouchableOpacity onPress={() => handleRemoveItem(item.id)}><TodoItem>{item.text}</TodoItem></TouchableOpacity>
                            }}
                        />
                    ) : (
                        <TodoItem>Nothing to do...</TodoItem>
                    )
                }
                <InputItem
                    value={todo}
                    onChangeText={(text) => handleChange(text)}
                />
                <TodoItem onPress={handleAddItem}>
                    <Text>Add Item</Text>
                </TodoItem>
                <TodoItem onPress={changeAuthStatus}>
                    <Text>Logout</Text>
                </TodoItem>
            </TodoBackground>
        )}
        return (
            <LoginView>
                <TodoItem onPress={changeAuthStatus}>
                    <Text>Login</Text>
                </TodoItem>
            </LoginView>
        )
}

export default ToDoList