網站首頁 編程語言 正文
本文屬于代碼分析,而非從0到1的代碼書寫。
本文會將每一段重要代碼的含義都分析清除,然后在代碼后面通過設定一些小問題,考驗一下大家是否對 “各種語句” 以及 “不同場景所需邏輯” 的使用已經掌握牢固。
? ?Redux
借助TS的Hooks操作數據,可以說是非常的簡便了,但是如果想要靈活地使用Hooks,了解React的Redux依舊是重中之
- 組件有各種行為,例如:增、刪、改、查。
- 組件不允許直接修改state,只能先創建出對應的action,里面記錄著行為的名稱和變化的數據,然后借助dispatch交給store。
- store將改變前的state和action交給reducer,reducer去操作具體的狀態,然后將新的state再存儲到store中,store將操作完的state再交給組件。
這就是redux的工作流程,或者是工作機制。
? ?整體結構
根據項目的結構,將項目分為上下兩個組件,上方是輸入組件Input,下方是展示組件List
- TodoList
- Input
- List
? ?src目錄結構
我們就根據代碼的引入順序,由外至內一步一步地分析關鍵的文件。
? ?App.tsx
// App.tsx
import React from 'react';
import TodoList from './components/TodoList';
function App() {
return (
<div className="App">
<TodoList />
</div>
);
}
export default App;
結構十分簡單,只需要引入TodoList,然后使用這個組件就可以。
? ?TodoList.tsx
import React, { FC, ReactElement, useCallback, useEffect, useReducer } from "react";
import TdInput from './Input';
import TdList from "./List";
import { todoListReducer } from "./reducer";
import { ACTION_TYPES, ITodoList, ITodo } from "./typings";
function initTL(initTodoList: ITodo[]): ITodoList{
return {
todoList: initTodoList
}
}
// 狀態惰性初始化,使用該方式,會等到useRuducer執行之后才去創建初始化的數據
const TodoList:FC = ():ReactElement=>{
const [state, dispatch] = useReducer(todoListReducer, [], initTL);
// 這里我們不使用useState去創建state,一方面是因為:對該數據的大量操作都位于子組件中,而非供自己使用
// 另一方面:是因為todoList中的數據涉及到增、刪、改,對數據的操作相對復雜,用useState無法對數據的深度修改進行優化
useEffect(() => {
const todoList = JSON.parse(localStorage.getItem('todolist') || '[]');
dispatch({
type: ACTION_TYPES.UPDATE_TODOLIST,
data: todoList
})
// dispatch中有我們的行為,以及對應處理的數據
}, [])
// 這個useEffect由于不對任何數據進行檢測,所以只會在頁面剛加載時執行一次。這個時候,我們需要從本地獲取數據
useEffect(() => {
localStorage.setItem('todolist', JSON.stringify(state.todoList));
}, [state.todoList])
// 當todoList發生變化時,向localStorage中保存最新的數據
const addTodo = useCallback((todo: ITodo)=>{
dispatch({
type: ACTION_TYPES.ADD_TODO, // 行為
data: todo // 修改的數據
})
// 當子組件觸發addTodo,需要向todoList中添加數據時,只需要告訴dispatch行為是添加,并把添加的數據放進去即可
}, [])
// 一個方法,如果使用者不是自己,而是供子組件使用,最好是用useCallback將其包裹起來【第二個參數為依賴】
const toggleTodo = useCallback((id: number)=>{
dispatch({
type: ACTION_TYPES.TOGGLE_TODO,
data: id
})
}, [])
const removeTodo = useCallback((id: number)=>{
dispatch({
type: ACTION_TYPES.REMOVE_TODO,
data: id
})
}, [])
return (
<div className="todolist">
<h1>TodoList</h1>
<!-- 將操作數據的方法和數據源交給子元素,由子元素決定在什么時候去添加元素 -->
<TdInput
addTodo={addTodo}
todoList={state.todoList}
/>
<br />
<!-- 在List中,包含了切換每一個item完成狀態、刪除item的操作,因此我們需要將兩個方法和數據源都傳遞過去 -->
<TdList
todoList={state.todoList}
toggleTodo={toggleTodo}
removeTodo={removeTodo}
/>
</div>
);
}
export default TodoList;
在這個組件中,我們需要弄明白6個核心要點:
- 什么是狀態惰性初始化?為什么要對初始化的狀態做惰性初始化處理?
- 什么時候用useState創建狀態?什么時候用useReducer創建狀態?
- useEffect依賴為空時,什么時候被觸發?依賴非空時,什么時候被觸發?
- 什么時候將數據存儲到localStorage中?什么時候取出來?
- dispatch里面傳了什么?它又在做什么?
- 我們需要向組件中傳遞什么?
? ?reducer.ts
import { ACTION_TYPES, IAction, ITodoList, ITodo } from "./typings";
function todoListReducer(preTodoList: ITodoList, action: IAction): ITodoList{
// 在dispatch中包裝的action,其實就是傳遞到了這里,所以這里的action包含了兩個屬性:type、data
// 這個preTodoList,是不需要我們主動傳遞一個實參和該形參對應的,因為實際并不是我們在調用的這個reducer,在reducer被調用時,會自動傳入一個原始的state,作為變化前的初始狀態
const {type, data} = action; // 取出行為的類型、修改的數據
// 根據不同的行為,對數據做不同的更改
switch(type){
case ACTION_TYPES.ADD_TODO:
return {
todoList: [...preTodoList.todoList, data as ITodo ] // 返回處理后的數據(原數據+新增的數據)
}
case ACTION_TYPES.REMOVE_TODO:
return {
todoList: preTodoList.todoList.filter(todo => todo.id !== data) // 過濾出與被刪除的item不同id的項
}
case ACTION_TYPES.TOGGLE_TODO:
return {
todoList: preTodoList.todoList.map(todo => { // 將被點擊的項的isCompleted取反,其他的項直接返回
return todo.id === data ?
{
...todo,
isCompleted: !todo.isCompleted
}:{
...todo
}
})
}
case ACTION_TYPES.UPDATE_TODOLIST:
return {
todoList: data as ITodo[] // 此時是頁面初始化,只需要將從localStorage中取出的數據裝入到store中即可
}
default:
return preTodoList;
}
}
export {
todoListReducer
}
這里需要明白4個核心
- todoListReducer在什么時候被調用?
- 我們有傳遞參數和preTodoList相對應嗎?
- action是哪里傳遞來的參數?它里面都有什么?
- 我們根據不同的行為,需要返回什么數據?是返回處理的單條數據todo,還是返回完整的數據TodoList?
? ?TdInput.tsx
import React, {useRef, FC, ReactElement} from "react";
import { ITodo } from "../typings";
// 接口:用來控制參數的種類和個數
interface IInputProps {
addTodo: (todo: ITodo) => void,
todoList: ITodo[]
};
const TdInput: FC<IInputProps> = ({
addTodo,
todoList
// 結構賦值,從父組件中傳遞來的參數其實都存儲在一個對象中
}): ReactElement =>{
const inputRef = useRef<HTMLInputElement>(null);
// ref可以用來標注標簽
const addItem = (): void=>{
const val: string = inputRef.current!.value.trim();
// "!" 為我們增加的斷言,表示這里一定可以拿到數據
const isExist = todoList.find(todo => todo.content === val);
if(isExist){
alert("該項已存在!");
return;
}
addTodo({
id: new Date().getTime(),
content: val,
isCompleted: false
})
// 在我們判定需要調用添加數據后,就可以調用addTodo了,如何傳參需要參考父組件,因為這個函數來自父組件,所以只有看了父組件是如何定義的,才能夠知道如何使用。
// 因為這個函數的行為是確定的,就是添加元素,所以我們就只需要在這里定義一個元素,然后作為函數的參數去調用函數就可以了。函數在定義時,也是這么設定的,只需要傳遞一個參數
inputRef.current!.value = '';
// 傳遞完參數,一定不能忘了將輸入框置空
}
return (
<div className="todo-input">
<input type="text" ref={inputRef}/>
<button onClick={addItem}>button</button>
</div>
)
}
export default TdInput;
這里面有5個關鍵點:
- 接口是用來限制誰的?
- 從父組件中傳遞過來的參數是一個對象?還是一個參數列表?
- 如何用ref來標注一個標簽?
- xxx.yyy.zzz,yyy一定有值,可是還是會提示yyy可能為undefined怎么辦?
- 從父組件傳遞過來的addTodo,我們需要向這個函數傳遞什么參數?
? ?TdList.tsx
import React, { FC } from "react";
import { ITodo } from "../typings";
import TdItem from "./Item";
interface IListProps{
todoList: ITodo[],
toggleTodo: (id: number) => void
removeTodo: (id: number) => void,
}
const TdList: FC<IListProps> = ({
todoList,
toggleTodo,
removeTodo
}) => {
return (
<div className="todo-list">
{
// ↓ 表示todoList 存在的話
todoList && todoList.map((todo: ITodo)=>{
return (
<TdItem
key={ todo.id }
todo = {todo}
toggleTodo={ toggleTodo }
removeTodo={ removeTodo }
/>
)
})
}
</div>
)
}
export default TdList;
這里就十分簡單了,因為我們將List又分為了一個個小的Item組件,所以List組件并沒有做太多事情。只是將數據進行簡單的遍歷,拿到一個個的數據todo,然后將這些數據再傳遞給TdItem組件,由該組件將一個個的數據創建成一個個Item組件。
但是這里我們一定不能忘了將TodoList組件傳遞來的toggleTodo、removeTodo方法傳遞給子組件,因為我們現在是在子組件上去調整對應的完成、刪除狀態的
? ?TdList.tsx
import React, { FC, ReactElement } from "react";
import { ITodo } from "../typings";
interface IItemProps{
todo: ITodo,
toggleTodo: (id: number) => void
removeTodo: (id: number) => void
}
const TdItem:FC<IItemProps> = ({
todo,
toggleTodo,
removeTodo
}):ReactElement => {
const {id, content, isCompleted} = todo
return (
<div className="todo-item">
<!--
在點擊方框時,需要改變todo的isComplete屬性
當isComplete為true時,就表示已完成,對應checked自然就是true,這個時候方框就是一個被勾選的狀態
-->
<input
type="checkbox"
onChange={ ()=>toggleTodo(id) }
checked={ isCompleted }
/>
<!-- 我們需要為已經完成的todo添加一個刪除線 -->
<span
style={ { textDecoration: isCompleted ? 'line-through' : 'none' } }
>{content}</span>
<!-- 當我們點擊刪除時,只需要刪除掉對應id的todo即可 -->
<button
onClick={ () => removeTodo(id) }
>刪除</button>
</div>
)
}
export default TdItem;
這里我們需要明白2個關鍵點:
- 如何讓我們的點擊,反饋到方框中?
- 如何讓我們的點擊,改變todo中的數據?
原文鏈接:https://blog.csdn.net/qq_44647809/article/details/121202763
相關推薦
- 2022-05-25 @Service未注入、 @Autowired未自動注入
- 2022-07-01 Go?函數中獲取調用者的函數名和文件名及行號_Golang
- 2022-05-28 Python實現孤立隨機森林算法的示例代碼_python
- 2022-10-14 Linux環境conda虛擬環境中python解釋器對應問題 + 解決后pip install 路徑
- 2022-04-17 C語言中用棧+隊列實現隊列中的元素逆置_C 語言
- 2022-05-12 linq中的串聯操作符_實用技巧
- 2022-07-28 Redis基本數據類型String常用操作命令_Redis
- 2023-10-15 AddressSanitizer 查找內存問題
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支