Introduction
You can get the source code here https://github.com/shivabhusal/todo-app-with-react-and-redux
React
React is spreading like a wildfire in Web Development Industry, as per
StackOverflow survey 2019.
React is used to build modern web UI in more manageable way. Moreover, I can say that now we
can utilize our programming skills to build UIs. Back in the days, UI developers used to
be the people with HTML / CSS / Bootstrap / Photoshop skill set. Those days, we didn't used to call UI development
a programming job.
Redux
Redux is tool to manage your application state more manageable and usable way you can imagine. Without
it you would have to pass data as props
deep down to the lower level components.
Basic Idea of Component Based Design
Lets start building a TODO app
We will use create-react-app
tool to create a new app. It will configure all necessary tool-chain
like babel, webpack and other.
npx create-react-app todo-app-with-redux
yes its npx
, (npx
is a package runner tool that comes with npm 5.2+
and higher, see instructions for older npm versions)
File Structures
src
├── App.css
├── App.js
├── App.test.js
├── index.css
├── index.js
├── logo.svg
├── serviceWorker.js
└── setupTests.js
Components
// src/components/visibilityFilter.jsx
import React from 'react'
export default function VisibilityFilter({ filters }) {
return (
filters.map((filter,i) => <button key={`filter-${i}`}>{filter}</button>)
)
}
// src/components/todoList.jsx
import React from 'react'
const Todo = ({ todo }) => <li>{todo.content}</li>
export default function TodoList({ todos }) {
return (
todos.map((todo, i) => (
<Todo key={i} todo={todo} />
))
)
}
// src/components/addTodo.jsx
import React from 'react'
export default function addTodo() {
return (
<>
<input type="text" placeholder="You text here" />
<button>Add</button>
</>
)
}
// src/App.jsx
import React from 'react';
import './App.css';
import AddTodo from './components/addTodo'
import TodoList from './components/todoList'
import VisibilityFilter from './components/visibilityFilter'
const filters = ['all', 'completed', 'incomplete']
function App() {
return (
<div className="App">
<h1>TODO Managers</h1>
<AddTodo/>
<TodoList todos={[{content: 'Task 1'}, {content: 'Task 2'}]}/>
<VisibilityFilter filters={filters}/>
</div>
);
}
export default App;
Now, since UI of the app is ready its time to make it alive.
Introducing Redux
Now, we need to introduce Redux
to feed data to the components and actions. We could
have used React Context API
but its more easier to use Redux
and pass action around.
We need the following new components:-
-
Action Types
: Types of all possible actions -
Action Creators
: Functions those take payload and prepare object withtype
andpayload
-
Reducers
: Functions that knows what to do with that action type. -
Containers
are created usingconnect()
function which exposes relevant state andaction
function. -
Store
: Application state; created usingcreateStore()
function that takes, combined reducers as arguments. -
Provider
wraps the entire app and it exposesstore
to the app.
// src/redux/actions.js
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO, SET_FILTER } from './actionTypes'
export const addTodo = (content) => (
{
type: ADD_TODO,
payload: {
content
}
}
)
export const toggleTodo = (id) => (
{
type: TOGGLE_TODO,
payload: {
id
}
}
)
export const deleteTodo = (id) => (
{
type: DELETE_TODO,
payload: {
id
}
}
)
export const setFilter = (filter) => (
{
type: SET_FILTER,
payload: {
filter
}
}
)
// src/App.css
button.active{
background-color: red;
}
li.completed{
text-decoration: line-through;
}
// src/redux/actionTypes.js
export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'
export const DELETE_TODO = 'DELETE_TODO'
export const SET_FILTER = 'SET_FILTER'
export const FILTER_ALL = 'all'
export const FILTER_COMPLETED = 'completed'
export const FILTER_INCOMPLETE = 'incomplete'
export const Filters = [FILTER_ALL, FILTER_COMPLETED, FILTER_INCOMPLETE]
// src/redux/reducers.js
import {FILTER_ALL} from './actionTypes'
import { ADD_TODO, TOGGLE_TODO, DELETE_TODO, SET_FILTER } from './actionTypes'
const initialTodoState = {
nextId: 2,
data:
{
1: {
content: 'Content 1',
completed: false
}
}
}
export const todos = (state = initialTodoState, action) => {
switch (action.type) {
case ADD_TODO: {
return (
{
...state,
data: {
...state.data,
[state.nextId]: {
completed: false,
content: action.payload.content
},
},
nextId: state.nextId + 1
}
)
}
case TOGGLE_TODO:{
console.log(action.payload)
return(
{
...state,
data:{
...state.data,
[action.payload.id]:{
...state.data[action.payload.id],
completed: !(state.data[action.payload.id].completed)
}
}
}
)
}
default: {
return state
}
}
}
export const visibilityFilter = (state = {activeFilter: FILTER_ALL}, action) => {
switch (action.type) {
case SET_FILTER: {
return ({
activeFilter: action.payload.filter
})
}
default: {
return state;
}
}
}
// src/redux/store.js
import {createStore, combineReducers} from 'redux'
import {todos, visibilityFilter} from './reducers'
export default createStore(combineReducers({todos, visibilityFilter}),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
// src/App.jsx
import React from 'react';
import './App.css';
import AddTodo from './components/addTodo'
import TodoList from './components/todoList'
import VisibilityFilter from './components/visibilityFilter'
import {Provider} from 'react-redux'
import store from './redux/store'
function App() {
return (
<Provider store={store}>
<div className="App">
<h1>TODO Managers</h1>
<AddTodo/>
<TodoList/>
<VisibilityFilter/>
</div>
</Provider>
);
}
export default App;
Updating the Ui Components to connect with Store
// src/components/addTodo.jsx
import React, { useState } from 'react'
import { connect } from 'react-redux'
import { addTodo } from '../redux/actions'
function AddTodo({ addTodo }) {
const [value, setValue] = useState('');
const handleOnChange = (e) => {
setValue(e.target.value)
}
const handleAdd = () => {
setValue('')
addTodo(value)
}
return (
<>
<input type="text" onChange={handleOnChange} value={value} placeholder="You text here" />
<button onClick={handleAdd}>Add</button>
</>
)
}
export default connect(null, { addTodo })(AddTodo)
// src/components/todoList.jsx
import React from 'react'
import { connect } from 'react-redux'
import { _ } from 'underscore'
import { FILTER_ALL, FILTER_COMPLETED } from '../redux/actionTypes'
import { toggleTodo } from '../redux/actions'
const Todo = ({ todo, id, toggleTodo }) => (
<li className={todo.completed ? 'completed' : ''} onClick={() => toggleTodo(id)}>{todo.content}</li>
)
function TodoList({ todos, toggleTodo }) {
return (
_.keys(todos).map((id) => (
<Todo key={id} id={id} toggleTodo={toggleTodo} todo={todos[id]} />
))
)
}
const mapState = (state) => {
if (state.visibilityFilter.activeFilter === FILTER_ALL) {
return { todos: state.todos.data }
} else if (state.visibilityFilter.activeFilter === FILTER_COMPLETED) {
return ({
todos: _.pick(state.todos.data, (todo) => todo.completed)
})
} else {
return ({
todos: _.pick(state.todos.data, (todo) => !todo.completed)
})
}
}
export default connect(mapState, { toggleTodo })(TodoList);
// src/components/visibilityFilter.jsx
import React from 'react'
import { connect } from 'react-redux'
import { setFilter } from '../redux/actions'
import { Filters } from '../redux/actionTypes'
function VisibilityFilter({ activeFilter, setFilter }) {
return (
Filters.map((filter, i) => (
<button
className={filter === activeFilter ? 'active' : ''}
onClick={() => setFilter(filter)}
key={`filter-${i}`}>
{filter}
</button>
))
)
}
const mapState = (state) => ({ activeFilter: state.visibilityFilter.activeFilter })
export default connect(mapState, { setFilter })(VisibilityFilter)
Read more from me:- https://shivab.com/blog/
Top comments (0)