这一节大概能对应文档的数据流结构
项目启动
大家好,经过rtk快速上手和前个视频的开发环境搭建,下面我们开始真正动手实践吖。
从文档的探索初始项目开始,但是稍微有区别,以视频为准吖。
首先按照文档建立项目结构,
然后安装必要的依赖库(注意,我用了v6 是v6版本的react-router吖)
npm install react-router react-router-dom -S
安装 react-router和react-router-dom
npm install @types/react-router @types/react-router-dom -D
安装对应的type 定义文件
emmm,在基本内容之外,其实我们主要建立/api和/app两个夹子(因为我们使用了文档,这两个夹子自动建立辣捏)
- /src
- /app
- store.ts 放置数据仓库
- hook.ts 放类型变换后的useDispatch 和useSelector
- /features 放置功能切片 (注意把示范用的counter切片删除。。或者仅仅留着好参考)
- index.ts 整个程序的入口文件
- App.tsx 应用的入口文件
- /api (虽然我没建立,但是按照文档示范,稍后会有这个东东)
- client.ts 小的 AJAX 请求客户端,用来发起 GET 和 POST 请求。
- server.ts 为我们的数据提供模拟的的 REST API。我们的应用程序将在稍后从这些模拟的接口获取数据。
- /app
如果您现在加载应用程序,您应该会看到标题和欢迎消息。我们还可以打开 Redux DevTools Extension,看到我们的初始 Redux 状态完全为空。
有了这个,让我们开始吧!
主页的文章列表
创建文章功能对应的slice
-
创建slice
-
在store中导入 reducer
展示文章列表
这里要建立一个PostList 组件,作用就是把store中存储的数据作为post 展示到页面上。
import React from 'react'
import { useAppSelector } from '../../app/hooks'
export const PostsList = () => {
const posts = useAppSelector(state => state.posts)
const renderedPosts = posts.map(post => (
<article className="post-excerpt" key={post.id}>
<h3>{post.title}</h3>
<p className="post-content">{post.content.substring(0, 100)}</p>
</article>
))
return (
<section className="posts-list">
<h2>Posts</h2>
{renderedPosts}
</section>
)
}
然后在App.tsx中放置一个路由,并引入PostList组件
import React from "react";
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
} from "react-router-dom";
import PostList from "./features/posts/PostList";
function App() {
return (
<Router>
<div className="App">
<Routes>
<Route path="/" element={<PostList />} />
<Route path="*" element={<div>
This is nowhere
</div>} />
</Routes>
</div>
</Router>
);
}
export default App;
现在,页面上应该出现这些东东辣。
添加新文章
先写一个增加post的 表单吖。
//features/posts/AddPostForm.tsx
import React, { useState } from "react";
export const AddPostForm = () => {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const onTitleChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setTitle(e.target.value);
const onContentChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
setContent(e.target.value);
return (
<section>
<h2>添加新文章</h2>
<form>
<label htmlFor="postTitle">文章标题:</label>
<input
type="text"
id="postTitle"
name="postTitle"
value={title}
onChange={onTitleChanged}
/>
<label htmlFor="postContent">内容:</label>
<textarea
id="postContent"
name="postContent"
value={content}
onChange={onContentChanged}
/>
<button type="button">保存文章</button>
</form>
</section>
);
};
然后更新下App.tsx,把 AddPostForm 放进页面捏!
现在页面上出现了新增文章的表单了噜
保存文章
好了,下面是从0到1的过程辣,要新增文章,真的好兴奋吖。
emmmm,实际上新增文章就是要用dispatch发起一个action,
action里边当然要放payload,然后store收到action,根据action,选取对应的reducer处理数据,然后改变状态,就是这个流程,喵。
还是要提一嘴,因为RTK,action不用手动编写了,我们只需要简单写处理数据的reducer就行了。
那么去到postSlice,新增一个addPost的 reducer捏。
slice 就大概下面酱紫:
import { createSlice } from "@reduxjs/toolkit";
const initialState = [
{ id: "1", title: "First Post!", content: "Hello!" },
{ id: "2", title: "Second Post", content: "More text" },
];
const postsSlice = createSlice({
name: "posts",
initialState,
reducers: {
postAdd:(state,action)=>{
state.push(action.payload)
}
},
});
export default postsSlice.reducer;
export const {postAdd}=postsSlice.actions
Dispatch "添加文章" Action
slice中的reducer写好了,最后只要绑定ui,在用户操作的时候dispatch一个action就行了捏,具体操作如下吖:
注意RTK提供的nanoid吖,这就是简化版的uuid吖
import React, { useState } from "react";
import { nanoid } from "@reduxjs/toolkit";
import { postAdd } from "./postSlice";
import { useAppDispatch } from "../../app/hooks";
export const AddPostForm = () => {
const [title, setTitle] = useState("");
const [content, setContent] = useState("");
const onTitleChanged = (e: React.ChangeEvent<HTMLInputElement>) =>
setTitle(e.target.value);
const onContentChanged = (e: React.ChangeEvent<HTMLTextAreaElement>) =>
setContent(e.target.value);
const dispatch = useAppDispatch();
const onSavePostClicked = () => {
if (title && content) {
dispatch(
postAdd({
id: nanoid(),
title,
content,
})
);
setTitle("");
setContent("");
}
};
return (
<section>
<h2>添加新文章</h2>
<form>
<label htmlFor="postTitle">文章标题:</label>
<input
type="text"
id="postTitle"
name="postTitle"
value={title}
onChange={onTitleChanged}
/>
<label htmlFor="postContent">内容:</label>
<textarea
id="postContent"
name="postContent"
value={content}
onChange={onContentChanged}
/>
<button type="button" onClick={onSavePostClicked}>
保存文章
</button>
</form>
</section>
);
};
现在,尝试输入标题和一些文本,然后单击“保存文章”。 您应该会在文章列表中看到该文章的新项目。
恭喜!你刚刚构建了你的第一个 React + Redux 应用程序!
这显示了完整的 Redux 数据流周期:
- 使用
useSelector
从 store 读取初始文章列表并渲染 UI- 我们 dispatch 了包含新文章条目数据的
postAdded
actionpostsReducer
监听到了postAdded
动作,并用新条目更新了 posts 数组- Redux store 告诉 UI 一些数据已经改变
- 文章列表读取更新后的文章数组,并重新渲染 UI 以显示新文章
在此之后我们将添加的所有新功能都将遵循相同的这个模式:添加状态 slice、编写 reducer 函数、dispatch action 以及基于 Redux store 中的数据渲染 UI。
标签:const,reducer,rtk,React,part2,文章,action,store From: https://www.cnblogs.com/nulixuexipython/p/17142337.html总结
- Redux state 由 reducer 函数来更新:
- Reducers 总是通过复制现有状态值,更新副本来不可变地生成新状态
- Redux Toolkit
createSlice
函数为您生成“slice reducer”函数,并让您编写 “mutable 可变”代码,内部自动将其转变为安全的不可变更新- 这些 slice 化 reducer 函数被添加到
configureStore
中的reducer
字段中,并定义了 Redux store 中的数据和状态字段名称- React 组件使用
useSelector
钩子从 store 读取数据
- 选择器函数接收整个
state
对象,并且返回需要的部分数据- 每当 Redux store 更新时,选择器将重新运行,如果它们返回的数据发生更改,则组件将重新渲染
- React 组件使用
useDispatch
钩子 dispatch action 来更新 store
createSlice
将为我们添加到 slice 的每个 reducer 函数生成 action creator 函数- 在组件中调用
dispatch(someActionCreator())
来 dispatch action- Reducers 将运行,检查此 action 是否相关,并在适当时返回新状态
- 表单输入值等临时数据应保留为 React 组件状态。当用户完成表单时,dispatch 一个 Redux action 来更新 store。