DEV Community

loading...

Kid's To-Do: Redux

fosteman profile image Timothy Fosteman ・3 min read

Let's create a To-Do app in Redux.

I want to start a component tree that can display list of toggle-able todos.

I'm gonna bootstrap Redux instance right inside React's default index.js, file management may come forth later in lifetime of this app. I'll write another post about storing your Redux multi-modular instance properly.

I'll build new root <TodoApp />, which will be wired up o the Redux state management.

Then passing properties down the tree: <TodoList /> with <TodoItem />. Latter will show the name of the todo and has a toggle on it's surface.

It's yet unreasonable to connect each of these components to Redux with connect, because they're not far away. However consider using this HOC in larger applications.

Pretty straigthforward. Make sure you've got internet connection, and a screwdriver in case your diskette is jammed.

Step 1: Begin with creating React app

npx create-react-app todo-app

cd todo-app

Install Redux

npm i --save redux

npm start

Step 2: Redux Instance

Modify index.js as follows to import Redux functionalities, provide Redux state to the component tree by wrapping it inside <Provider />. TodoApp component is connected with connect HOC, exposing state and dispatcher to the tree

import { combineReducers, createStore } from 'redux'
import { Provider } from 'react-redux'
import TodoApp from './todoApp'

/*
* action types 
* initial state
* reducers
* action creators
* reducer combination
* store declaration
*/

function mapStateToProps(state) {
  return {
    todos: state.todoState,
  };
}
function mapDispatchToProps(dispatch) {
  return {
     onToggleTodo: id => dispatch(doToggleTodo(id)),
  };
}

const ConnectedTodoApp = connect(mapStateToProps, mapDispatchToProps)(TodoApp);

ReactDOM.render(
  <Provider store={store}>
    <ConnectedTodoApp />
  </Provider>,
  document.getElementById('root')
);

Action Types

// action types
const TODO_ADD = 'TODO_ADD';
const TODO_TOGGLE = 'TODO_TOGGLE'; 
const FILTER_SET = 'FILTER_SET';

Initial State

// initial state
const todos = [
    { id: '0', name: 'Outline blog post' }, 
    { id: '1', name: 'Build TodoApp' },
];

Reducers

// reducers
function todoReducer(state = todos, action) {
    switch(action.type) {
        case TODO_ADD: {
            return applyAddTodo(state, action);
        }
        case TODO_TOGGLE: {
            return applyToggleTodo(state, action); 
        }
        default : return state; 
    }
} 

function applyAddTodo(state, action) {
    const todo = Object.assign({}, action.todo, {completed: false});
    return state.concat(todo);
}

function applyToggleTodo(state, action) {
    return state.map(todo => todo.id === action.todo.id ?
        Object.assign({}, todo, {completed: !todo.completed})
        : todo
    );
}

function filterReducer(state = 'SHOW_ALL', action) {
    switch(action.type) {
    case FILTER_SET: {
        return applySetFilter(state, action);
    }
    default: return state;
    }
}

function applySetFilter(state, action) {
    return action.filter;
}

Action Creators

// action creators
function doAddTodo(id, name) {
    return {
        type: TODO_ADD,
        todo: {id, name}
    };
}

function doToggleTodo(id) {
    return {
        type: TODO_TOGGLE,
        todo: { id }
    };
}

function doSetFilter(filter) {
    return {
        type: FILTER_SET,
        filter
    };
}

Finally, let's combine todoReducer with filterReducer and create store

const rootReducer = combineReducers({
    todoState: todoReducer,
    filterState: filterReducer
});

const store = createStore(rootReducer);

Having installed Redux, let's build To-Do application component tree starting with a new root TodoApp.

Step 3: Components

TodoApp

import React from 'react'
import TodoList from './todoList'

export default function TodoApp({ todos, onToggleTodo }) { 
    return (<TodoList
                    todos={store.getState().todoState}
                      onToggleTodo={id => store.dispatch(doToggleTodo(id))}
                  />);
}

TodoList

import React from 'react'
import TodoItem from './todoItem'

export default function TodoList({ todos, onToggleTodo }) { 
    return (
    <div>
      {todos.map(todo => 
                <TodoItem
            key={todo.id}
            todo={todo}
            onToggleTodo={onToggleTodo}
                />)} 
        </div>
    ); 
}

TodoItem

import React from 'react'

export default function TodoItem({ todo, onToggleTodo }) { 
    const { name, id, completed } = todo; 
        return (
            <div> {name}
            <button
            type="button"
            onClick={() => onToggleTodo(id)}
          >
        {completed ? "Incomplete" : "Complete"}
                </button>
            </div>
        );
}

None of these components are aware of Redux. They simply display todos and use callbacks to propagate toggle of todo items.

The store does two things: it makes state accessible and exposes functionalities to alter the state. The todos props are passed down to the TodoApp by retrieveing them from the store instance. In addition, onToggleTodo function is passed down the tree as a property, notice that it's a HOC that wraps the dispatching of an action that is created by its action creator.

Discussion

pic
Editor guide