Skip to main content

Simple Redux

TST, Hongkong

Setup

npm init -y && tsc --init
npm install --save-dev webpack webpack-cli webpack webpack-dev-server
npm install --save-dev @types/node @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint typescript ts-loader
npm install redux @redux-devtools/extension

./tsconfig.json

{
"include": ["./src"],
"exclude": ["./node_modules", "./src/bak"],
"compilerOptions": {
"target": "es6",
"jsx": "react",
"module": "es6",
"moduleResolution": "node",
"allowJs": true,
"sourceMap": true,
"outDir": "./public/src/",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

./package.json

{
"name": "react-redux-2024",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"tsc": "tsc --watch",
"dev": "node public/index.js",
"serve": "webpack serve",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/lodash": "^4.14.202",
"@types/node": "^20.11.5",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"eslint": "^8.56.0",
"ts-loader": "^9.5.1",
"typescript": "^5.3.3",
"webpack": "^5.89.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1"
},
"dependencies": {
"lodash": "^4.17.21"
}
}

./webpack.config.js

const path = require('path');

module.exports = {
entry: './src/index.ts',
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'public'),
},
devServer: {
static: {
directory: path.resolve(__dirname, 'public'),
},
hot: true,
port: 3000
},
devtool: "source-map",
mode: 'development'
};

Basic Redux Todo List

./src/store/tasks/actionTypes.js

export const ADD_TASK = "ADD_TASK"
export const TASK_DONE = "TASK_DONE"
export const REMOVE_TASK = "REMOVE_TASK"

./src/store/tasks/actions.ts

import * as actionTypes from './actionTypes'

export const addTask = (task: string) => {
return {
type: actionTypes.ADD_TASK,
payload: {
task: task
}
}
}

export const taskCompleted = (id: number, completed: boolean) => {
return {
type: actionTypes.TASK_DONE,
payload: {
id: id,
completed: completed
}
}
}

export const removeTask = (taskid: number) => {
return {
type: actionTypes.REMOVE_TASK,
payload: {
id: taskid
}
}
}

./src/store/tasks/reducer.js

import * as actionTypes from '../actions/actionTypes'

let id = 0

export function taskReducer(state = [], action) {
switch(action.type) {
case actionTypes.ADD_TASK:
return [
...state,
{
id: ++id,
task: action.payload.task,
completed: false
}
]
case actionTypes.TASK_DONE:
return state.map(task => task.id === action.payload.id ? {
...task, completed: true
} : task)
case actionTypes.REMOVE_TASK:
return state.filter(task => task.id !== action.payload.id)

default:
return state
}
}

./src/store/configuration .js

import { legacy_createStore as createStore} from 'redux'
import { composeWithDevTools } from '@redux-devtools/extension';
import { taskReducer } from './tasks/reducer'

const composeEnhancers = composeWithDevTools({
trace: true,
});

const store = createStore(
taskReducer,
/* preloadedState, */ composeEnhancers(),
// window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
// https://github.com/reduxjs/redux-devtools/tree/main/extension#installation
)

export default store

Now with the store build and the reducer defined you can add the following to:

./src/index.ts

import store from './store/configuration
'
import { addTask, removeTask, taskCompleted } from './store/tasks/actions'

const unsubscribe = store.subscribe(() => {
console.log("State updated ::", store.getState())
})

store.dispatch(addTask('Hello Task!'))
store.dispatch(taskCompleted(1, true))
store.dispatch(removeTask(1))
unsubscribe()

This will create a new task and store it inside your store, update it's completion state and then remove it. Check your Browser console for the log output:

State updated :: [
{
"id": 1,
"task": "Hello Task!",
"completed": false
}
]

State updated :: [
{
"id": 1,
"task": "Hello Task!",
"completed": true
}
]

State updated :: []