Todo App - ReactJs+Redux

Ozan Aydın
7 min readJun 7, 2020

In this part, we will make a simple todo application using ReactJs, Redux and design which we made at Part1. On the backend side, we will use an API that I wrote earlier. As a result of Part1 and Part2, we will reach Figma + Mern Stack but backend is not our topic for now. If you want to check backend codes anyway visit my github repository.

If you want to check codes about this blog visit here on my github page.

We will try to keep it simple and clear. Before start coding i want to share some information about the concepts you will hear often and we will use.

First things first..

ReactJS: A JavaScript library for building user interfaces.

ReactJs is an open-source library developed by Facebook (initial release 2013) and is currently maintained with open source community.

Create React App: A comfortable environment for learning React, and is the best way to start building a new single-page application in React.

It offers a modern build setup with no configuration.

Redux: A Predictable State Container for JS Apps.

Redux was created by Dan Abramov and Andrew Clark in 2015. They are in React Core Team at Facebook(for now). Most of people think Redux for only ReactJS but Redux can be used as a data store for any UI layer.

If you think that ‘Do i need Redux every Reactjs project?’ you don’t. Dan Abramov says “Don’t use Redux until you have problems with vanilla React.”

In general, use Redux when you have reasonable amounts of data changing over time, you need a single source of truth, and you find that approaches like keeping everything in a top-level React component’s state are no longer sufficient.

P.3: Redux Flow

Action: Actions are payloads of information that send data from your application to your store. They must have a type property that indicates the type of action being performed. They are the only source of information for the store. You can also hear action creator, it is functions that create actions.

Simple Action: 
{ type: ‘ACTION_TYPE’ , payload: ‘data’}
Action Creator:
function actionCreator(data) {
return { type: ‘ACTION_TYPE’, payload: ‘data’}}

Reducer: Reducers specify how the application’s state changes in response to actions sent to the store. Actions only describe what happened, but don’t describe how the application’s state changes. The reducer is a pure function that takes the previous state and an action, and returns the next state.

function simpleReducer(state = defaultState, action) {
switch (action.type) {
case 'ACTION_TYPE':
return newstate
case 'ANOTHER_ACTION_TYPE':
return newstate
default:
return state
}
}

Store: Holds all your application’s state. Any dispatched action returns a new state to the store via reducers. It’s important to note that you’ll only have a single store in a Redux application.

Let’s Start Coding

Creating our app which name is ‘todo’ with Create React App

yarn create react-app todo

It gives us a project, it’s file structure like this:

P.1: Create react-app file structure

I change that file structure like i feel comfortable and what I needed when coding, there is no only one correct way about it. You can change it how you feel right.

P.2: My file Structure

Index.html, index.js and then here we go from our App.js. The <Provider /> makes the Redux store available to any nested components so we should wrapp our App. I won’t write all codes here, you can check all codes from here. Also I won’t add import codes.

function App() {
return (
<Provider store={store}>
<BrowserRouter>
<Layout />
</BrowserRouter>
</Provider>
);
}

export default App;

We wrapp our app with store but where is our Store? Here is.

const middlewareEnhancer = applyMiddleware(thunk);
const composedEnhancers = composeWithDevTools(middlewareEnhancer);


const store = createStore(rootReducer, undefined, composedEnhancers)

export default store;

A store creator is a function that creates a Redux store. Like with dispatching function, we must distinguish the base store creator, createStore(reducer, preloadedState) exported from the Redux package, from store creators that are returned from the store enhancers.

const store = createStore(reducer, preloadedState, enhancer)

Redux Thunk is a middleware that lets you call action creators that return a function instead of an action object.

We have a design and i focus to make same with design as a Layout. Basically our Layout.js will be like that.

const Layout = () => {

return(
<div className="main">
<div className="todo-button">

<Switch>
<Route path="/todo" component={Todo} />
<Route path="/list" component={List} />
</Switch>

<div className="list-button">
<div className="footer">by ozanaydin</div>
</div>
)
};
export default Layout;

Router will be change our todo and list boxes. Todo component has a input area and add button. List component has a TodoList component as a child which contains textarea and delete button.

Now let’s create Actions.js and TodoReducers.js. After that we will use them in our components for adding and listing todos.

export function todosAdd(text) {    return async (dispatch) => {
dispatch({
type: 'TODOS_ADD'
})

try {
const res = await fetch('endpoint and request details')
if(res.ok) {
alert("todo added successfully");
dispatch({
type: 'TODOS_ADD_SUCCESS',

payload: text
})
}
} catch (error) {
dispatch({
type: 'TODOS_ADD_FAILED',
payload: error
})
}
}
}

As you can see we have type in action, this will match with Reducer. Reducer will take payload from Action, will take the previous state and will send next state to store. Let’s see our TodoReducers.js:

const defaultState = {
isLoading: null,
error: null,
todos: [],
}

const TodoReducers = (state = defaultState, action) => {
switch (action.type) {

case 'TODOS_ADD_SUCCESS':
return { ...state, isLoading: false,
todos: [...state.todos,
{todo_completed: false,todo_name:action.payload}]
};

default:
return state
}
}

export default TodoReducers

If our action type is TODOS_ADD_SUCCESS, Reducer matches with that case and returns new state. Important thing is here never mutating state because of that we use …state. The object spread operator is conceptually similar to the ES6 array spread operator. It lets you to copy enumerable properties from one object to another in a more succinct way. In here, we got new object which same previous state and added only new todo text (todo_name: action.payload) to that object.

We have more action types for deleting, updating and getting todos in TodoReducers. But in this app we have one Reducer which name is TodoReducers. If you need more than one Reducer, for example x, y, z, you should combine them like this:

export default combineReducers({TodoReducers, x, y, z})

Let’s fire an action in Todo.js and see what’s happening:

const Todo = () => {

const dispatch = useDispatch();
const [text, setText] = useState([]);

function addTodo() {
dispatch(todosAdd(text))
}

return(

<div className="todo-box">
<div className="todo-area">
<textarea className="textarea"
onChange={(e) => setText(e.target.value)}>
</textarea>
</div>
<div className="button-area">
<button className="add-button"
onClick={addTodo}>
<span className="add-button-text">ADD</span>
</button>
</div>
</div>
)
}

export default Todo;

When we click Add button, we getting text value from textarea and dispatching todosAdd(text) action creator with useDispatch hook. Reducer matchs with action’s type and take action’s payload then returns new state to store.

When coding Redux, using Redux Dev Tools is getting easier the things for you. You can see the all changes:

P.3: Redux DevTools

Finally take a look our todos in List.js:

const List = () => {

const dispatch = useDispatch();
const todos = useSelector(selectTodos)

useEffect(() => {
dispatch(getTodos())
}, [dispatch])

return (
<div className="list-box">
<div className="list-area">
{todos.map((t) => (
<TodoList key={t._id} {...t} />
))}
</div>
</div>
)
}

export default List;

We use the useSelector hook to get the state from the Redux store in a React component. You can write accessors like “selectTodos” for not writing inside component.

export const selectTodos = (state) => state.TodoReducers.todos;

With this way you can use simple use useSelector(selectTodos).

In List.js also we have a child <TodoList /> component as you see. Inside TodoList.js we have a text area, which shows our todo and a text area which has x value for deleting todo. Our text areas will work like buttons. They will have onClicks.

When we click our todo text area, we will change todo-completed status. To change todo completed status, we will use todoUpdate(_id, todo_completed) action creator. If todo is completed, the text will has “list-text-done” css class and text will has a strikethrough.

If we click x button then we will delete that todo. To delete todo, we will use todoDelete(_id) action creator.

const TodoList = ({todo_name, todo_completed, _id}) =>{

const dispatch = useDispatch();

return(
<>
{(todo_completed) ? <textarea readOnly={true} onClick= {() => dispatch(todoUpdate(_id,todo_completed))} className="list- text-done" defaultValue={todo_name}></textarea>
: <textarea readOnly={true} onClick={() => dispatch(todoUpdate(_id,todo_completed))} className="list-text" defaultValue={todo_name}></textarea>}

<textarea readOnly={true} className="list-delete" onClick={() => dispatch(todoDelete(_id))} defaultValue={"x"}></textarea>
</>
)};

export default TodoList;

--

--