在上一章Todolist with React (2),完成所有样式设定後,现在就让我们在 React 中加入 Redux,使用 React-redux 动态产生任务清单。
将状态资料分成 Todo 任务资料和 Filter 筛选器资料,事先建立好 Store、Reducer、Action 的档案。
src
├── ...
├── actions
├── ActionTypes.js
├── filter.js
├── todos.js
├── reducer
├── filter.js
├── index.js
├── todos.js
├── store.js
首先先处理渲染任务资料的部分,在 todos.js 定义出原始的任务资料 initialTasks
,包含任务的名称和状态,方便我们产生清单。
reducer/todos.js
const initialTasks = [
{ taskName: "task1", isCompleted: false },
{ taskName: "task2", isCompleted: true },
{ taskName: "task3", isCompleted: false },
];
继续在同一份程序码建立 Todo Reducer,State 参数的预设值为之前定义的 initialTasks
,目前尚未定义 Action,直接回传当前原本的 State。
reducer/todos.js
import * as types from '../actions/ActionTypes';
export default function todos(state = initialTasks, action) {
switch (action.type) {
default:
return state;
}
}
因为我们会有两份 Reducer,要用 combineReducers
将两者合并,目前还没做到 Filter Reducer,这边就先填上 Todo Reducer。
reducer/index.js
import { combineReducers } from 'redux';
import todosReducer from './todos';
const todoApp = combineReducers({
todosReducer
});
export default todoApp;
再来建立 Store 引入 rootReducer,透过 <Provider>
的方式来传递 Store 让网页能够读取更新状态。
src/store.js
import { createStore } from "redux";
import rootReducer from "./reducer/index";
const store = createStore(rootReducer);
export default store;
src/index.js
...
import { Provider } from "react-redux";
import store from "./store";
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
...
);
将 Store 传入 React Component 後,TaskList Component 就可以使用 useSelector
取得任务清单。原本我们是直接复制三个 <TaskItem />
产生三个任务,修改成利用 forEach
读取 Store 的动态产生任务,并且把任务资料和编号 task={{ ...item, idx: index }}
传入 <TaskItem />
:
components/TaskList.js
...
import { useSelector } from "react-redux";
function TaskList() {
const tasks = useSelector((store) => store.todosReducer);
const renderItems = () => {
let list = [];
tasks.forEach((item, index) => {
list.push(
<TaskItem key={item.taskName} task={{ ...item, idx: index }} />
);
});
return list;
};
return (
<Wrapper>
...
<TaskItemContainer>{renderItems()}</TaskItemContainer>
</Wrapper>
);
}
components/TaskItem.js
function TaskItem(props) {
return (
<Container>
<CheckBox
type="checkbox"
checked={props.task.isCompleted}
/>
<TaskName>{props.task.taskName}</TaskName>
<Button>Delete</Button>
</Container>
);
}
所有任务都能动态渲染後,再来处理任务新增 / 删除的功能,在 ActionTypes.js 定义 ADD_TASK
和 DELETE_TASK
,这样可以统一方便管理所有 Action。
actions/ActionTypes.js
export const ADD_TASK = 'ADD_TASK';
export const DELETE_TASK = 'DELETE_TASK';
Step1:建立 Action Creator 产生新增 Task 的动作 Function addTask
,并取得新增的任务名称 taskName
。
actions/todos.js
import * as types from './ActionTypes';
export function addTask(taskName){
return {
type: types.ADD_TASK,
taskName
};
}
Step2:处理 Todo Reducer,读取 action.type 为 ADD_TASK
时,储存新任务的资料并回传新的 State。
reducer/todos.js
import * as types from '../actions/ActionTypes';
const initialTasks = ...
export default function todos(state = initialTasks, action) {
switch (action.type) {
case types.ADD_TASK:
return [
...state,
{
taskName: action.taskName,
isCompleted: false,
},
];
default:...
}
}
Step3:完成 Action 和 Reducer 的设定後,在 AddTask Component 利用 useDispatch()
来调用 Action。点击 <AddBtn />
後呼叫 handleClick()
,处理新增任务的程序。
components/AddTask.js
import { useDispatch } from "react-redux";
function AddTask() {
const dispatch = useDispatch();
const [newTask, setnewTask] = ...
const handleChange = ...
const handleClick = (event) => {
if(newTask === "") return; //检查有没有输入任务名称
dispatch(actions.addTask(newTask));
setnewTask("");
};
return (
<Wrapper>
...
<AddBtn onClick={() => handleClick()} >
<img src={addIcon} alt=""/>
</AddBtn>
</Wrapper>
);
}
Step1:删除 Task 的部分也大同小异,第一步建立 Action Creator 产生删除 Task 的动作 Function deleteTask
,并取得要删除的任务索引值 idx
。
actions/todos.js
export function deleteTask(idx){
return {
type: types.DELETE_TASK,
idx
};
}
Step2:处理 Todo Reducer,读取 action.type 为 DELETE_TASK
时,删除该任务的资料并回传的新 State。
actions/todos.js
export default function todos(state = initialTasks, action) {
switch (action.type) {
case types.ADD_TASK:...
case types.DELETE_TASK:
return [
...state.slice(0, action.idx),
...state.slice(action.idx + 1)
];
default:...
}
}
Step3:完成 Action 和 Reducer 的设定後,在 TaskItem Component 利用 useDispatch()
来调用 Action。点击 <Button/>
後直接呼叫 deleteTask()
,传入要删除的任务索引值。
components/TaskItem.js
import { useDispatch } from "react-redux";
function TaskItem(props) {
const dispatch = useDispatch();
return (
<Container>
...
<Button onClick={() => dispatch(actions.deleteTask(props.task.idx))}>
Delete
</Button>
</Container>
);
}
今天完成了渲染任务清单、和任务新增删除的动作,在下一篇文章,我们会继续完成最後一个部分 — Filter 筛选器,那我们就明天见罗!
如果文章中有错误的地方,要麻烦各位大大不吝赐教;喜欢的话,也要记得帮我按赞订阅喔❤️
<<: 爬虫怎麽爬 从零开始的爬虫自学 DAY27 python网路爬虫开爬8-储存问题解决
>>: IT 铁人赛 k8s 入门30天 -- day27 Communicate Between Containers in the Same Pod Using a Shared Volume
今天要介绍的是可以用来操作 DOM 元素的 useRef 及和它有关的 useImperativeH...
前面几篇做了几个交易相关的API,而为了快速跳过後端API的建置,前端网页要留存的资料源,就打算偷懒...
铁人赛终於要过一半了,今天要来介绍 Go 的特色之一 -- Goroutines。 那麽话不多说,我...
08 - Preitter output in rails console Rails 的 defa...
您的订阅是我制作影片的动力 订阅点这里~ 影片程序码(延续昨天) #步骤二: 资料分群,哪个演算法?...