react
jsx 语法
js 内嵌入 的 html 标签 就是 jsx 语法。 html 标签内嵌入 js。 jsx 内写 js 直接使用 {}。 jsx 内标签的属性有几个需要特殊记忆 1. class 写成 className 2. lable 内的 for 写成 htmlFor 3. 自定义属性名写成小驼峰 4. 标签内无内容写成单闭合
返回多个根节点
- 默认只能返回一个根节点
- 可以结束 Fragments 返回多个节点 <></> 就是空标签 不会渲染任何标签 空标签是简写
组件
- 函数组件 根据 props 展示 html 内容
- 类组件 做功能
- 区别:
- 有无 state 类组件有
- 有无 声明周期 类组件有
- 有了 hook 后 函数组件内也可以使用 state 和声明周期
props
- 功能:组件复用的时候父组件给子组件传递数据
- 注意: props 为只读的不可更改
函数组件内使用 props
- 函数组件的函数内默认接收一个参数交 props,参数 props 内存储的就是父组件传递的 props 对象,props 为只读的不可更改
类组件内使用 props
- render 函数默认会触发
- 组件的 this 上会接收父组件传递的 props,props 为只读的不可更改
render() {
// render函数默认会触发
// 组件的this上会接收父组件传递的props,props为只读的不可更改
const { text } = this.props;
return <button onClick={this.handleClick}>{text}</button>;
}
export default function Button(props) {
const { text } = props;
return <button>{text}</button>;
}
事件处理
1. 必须赋值一个函数,不能是函数调用
<button onClick={login_test}>事件绑定测试</button>
2. 事件传参
不能将 login_test 当作事件函数,写一个新的函数,函数内执行 login_test 并传递参数,新的函数是事件函数
<button
onClick={() => {
login_test("哈哈哈哈");
}}
>
事件传参
</button>
3. 类内的事件绑定
类内创建的公共方法就可以当作函数的使用,比如事件函数
export default class Button extends Component {
handleClick = () => {
console.log("我是按钮点击事件");
};
render() {
const { text } = this.props;
return <button onClick={this.handleClick}>{text}</button>;
4. 事件对象获取
- 没有传递事件参数可以直接在形参中获
- 传递事件参数
<button
onClick={(e) => {
login_test("哈哈哈哈", e);
}}
>
事件传参
</button>
类组件中的 this 问题
- 非声明周期函数(render 等)内想要使用 this 的话需要创建成箭头函数
export default class Button extends Component {
handleClick = () => {
console.log("我是按钮点击事件");
console.log(this.props);
};
}
- 利用 bind 解决
<button onClick={this.handleClick.bind(this)}>{text}</button>
state
状态变页面变,页面变化需要 state 控制
更改 state 的值
-
修改 state 不能直接修改 需要使用 setState 方法修改 这个方法是 Component 类自带的,所以所有组件都可以使用
-
使用 setState 的时候传递一个对象,对象内的属性写的就是要修改的具体的 state
-
切记
不能直接修改state
-
setState 何时同步何时异步?
由 React 控制的事件处理程序,以及生命周期函数调用 setState 不会同步更新 state 。
React 控制之外的事件中调用 setState 是同步更新的。比如原生 js 绑定的事件,setTimeout/setInterval/async await 等。 -
更改后想要获取新的值,加一个定时器
state = {
count: 100,
};
// count++
this.setState({
count: this.state.count + 1, //不能写++ ++是更改并赋值操作直接就改了state
});
- 更改数组或这对象的时候不能只更改其中一个的值,需要将整个对象或者数组重新赋值。可以利用展开运算符简化
state = {
user: {
userName: "xm",
userAge: 18,
},
};
changeAge = () => {
/* this.setState({
user: {
userName: "xm",
userAge: 20,
},
}); */
this.setState({
user: { ...this.state.user, userAge: 20 },
});
};
组件消失出现
- 样式消失
<div className="box" style={{ display: show ? "block" : "none" }}></div>
- 真正消失
{
show ? <div className="box"></div> : "";
}
// 当在 html 内写 undefined null '' false 的时候不会渲染任何内容
{
show && <div className="box"></div>;
}
列表渲染
- react 的 jsx 内可以直接将数组内的值渲染到页面上
- 列表渲染 其实就是将数组数据变为 html 数组数据,然后直接渲染到页面上
state = {
arr: [1, 2, 3, 4, 5],
};
const { count, show, arr } = this.state;
<ul>
{arr.map((el) => (
<li key={el}>{el}</li>
))}
</ul>;
react 生命周期
- 生命周期函数不写成箭头函数也可以获取 this
1.挂载 初始的渲染阶段 执行顺序从上到下
- constructor() 类的初始化,1.初始化 state 2.为事件处理函数绑定实例(es6 的箭头函数事件无需绑定实例) 用的很少
- 它接受两个参数:props 和 context,当想在函数内部使用这两个参数时,需使用 super()传入这两个参数。
- 注意:只要使用了 constructor()就必须写 super(),否则会导致 this 指向错误。
- static getDerivedStateFromProps() 从 props 中获取 state 这个生命周期的功能实际上就是将传入的 props 映射到 state 上面
- static 生命的方法是 类的私有方法,只能在类内使用,类的实例无法调用
- render() 必须返回页面内容,主要作用提供虚拟 dom
- componentDidMount() 页面挂载完毕,组件的 html 渲染到页面上. 在这里面可以直接调用 setState,所以在这里
发请求拿数据
2.更新 当组件的 state 或者 props 发生改变的时候就会触发组件的更新
- static getDerivedStateFromProps() 用的很少
- shouldComponentUpdate() 在更新的时候可以使用该函数阻止更新 必须返回一个布尔值,该函数接收两个参数 新的 props 新的 state
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate() 组件更新 state 或者 props 完毕,页面渲染完毕。在这个函数内也可以修改 state,但是必须写一个条件句去修改,否则会无限循环
3.卸载
- componentWillUnmount()
表单
受控组件
- 在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。
- 需要绑定 state 值和 onChange 事件 onChange 事件是输入了就触发,并不是失去焦点才触发
- 可以设置一个公共的更改表单的方法,根据 name 属性来更改 state 的值
state = {
username: "xy",
};
changeForm = (e) => {
this.setState({
// 根据name属性去更改表单的值
[e.target.name]: e.target.value,
});
};
<input
name="username"
type="text"
value={username}
onChange={this.changeForm}
/>;
- 多选框
state= {
likes: ["vue", "react"],
}
// 更改多选
changeLikes = (e) => {
const target = e.target;
this.setState({
// 如果为true,在likes中追加这个元素的value,为false,likes中去除这个元素
likes: target.checked
? [...this.state.likes, target.value]
: this.state.likes.filter((el) => el !== target.value),
});
};
<h4>你喜欢的框架有哪些</h4>
<label htmlFor="">vue</label>
<input type="checkbox" value="vue" checked={likes.includes("vue")}
onChange={this.changeLikes} />
<br />
<label htmlFor="">react</label>
<input type="checkbox" value="react" checked={likes.includes("react")}
onChange={this.changeLikes} />
<br />
<label htmlFor="">angular</label>
<input type="checkbox" value="angular" checked={likes.includes("angular")}
onChange={this.changeLikes} />
<br />
<label htmlFor="">微信小程序</label>
<input type="checkbox" value="mini" checked={likes.includes("mini")}
onChange={this.changeLikes} />
<br />
非受控组件
- 非受控组件 不使用 state。 初始展示效果的话,需要使用 defaultValue 或者 defaultChecked 等展示,不直接使用 value 等,获取该元素的状态的话,使用 ref 获取
ref 的使用
- 先用 createRef 创建
- 在元素上使用
- 获取元素实例对象需要使用 curent 方法
- 在元素身上获取元素对象,在组件身上获取组件实例对象
import { Component, createRef } from "react";
checkBox = createRef();
<input type="checkbox" defaultChecked={true} ref={this.checkBox} />;
// 获取组件实例
checkBox.current;
解析 html
- 想要解析 html 字符串 需要使用 dangerouslySetInnerHTML 属性,该属性的值是一个对象,对象的
__html
属性是 html 字符串的话会被解析到该元素内
<div dangerouslySetInnerHTML={{ __html: content }}></div>
children prop
当父组件传递的内容在子组件双标签之间的话,那么子组件或使用 children prop 来接收
- 当组件自己无法确定自己的部分内容的展示,需要父组件提供
- 此时我们就可以使用父组件 传递得 childern prop 来实现
- 比如 对话框组件
// children prop 存储的是父组件在使用子组件的时候标签之前传递的html内容
// 当内容是一个节点(react.element)的话,那么children指的就是这个节点
// 当内容是多个节点(react.element)的话,children是个数组,存储这些节点
// 因为 react 可以直接写 jsx 语法,所以children prop可以直接传递html标签
// 那么多个节点处理的时候 children用起来就没有 prop方便了
const { children, dialogContent } = this.props;
状态提升
组件间的交互,将公共的状态提升就是用户对子组件操作,子组件不改变自己的状态,通过自己的 props 把这个操作改变的数据传递给父组件,改变父组件的状态,从而改变受父组件控制的所有子组件的状态,这也是 React 单项数据流的特性决定的。官方的原话是:共享 state(状态) 是通过将其移动到需要它的组件的最接近的共同祖先组件来实现的。 这被称为“状态提升(Lifting State Up)”。
propTypes 类型检查
就是在使用组件的时候 给组件传递特定的 props 组件才会展示对应的内容,那么如何约束父组件传递的 props,就是 propTypes 类型检查
使用方法
- 导入 prop-types
import PropTypes from "prop-types"
; PropTypes 这个包来处理类型检查,这个包已经默认下载好了,无需自己下载 - 类型检查 类.propTypes = {}
- isRequired 必传
PropTypesDemo.propTypes = {
title: PropTypes.string,
description: PropTypes.string.isRequired, //必传
};
设置默认值
- 类.defaultProps = {}
PropTypesDemo.defaultProps = {
title: "默认标题",
};
context
context 用于父元素向后代元素传递数据
用法
父组件
- 引入 createContext
import { createContext } from 'react'
- 根据 createContext 创建一个 context 上下文数据,并赋值初始值 创建好的 context 内有两个组件 Provider 提供者, Consumer 消费者 export const {Provider, Consumer} = createContext(’#00b3d4’);
- 父元素向后代提供
state = {
themeColor: "blue",
};
changeColor = (color) => {
this.setState({
themeColor: color
})
// changeColor为更改属性函数
<Provider value={{ themeColor, changeColor: this.changeColor }}>
{/* 利用 Provider 组件将 value 的值传递给 son 组件以及后代组件 */}
<Son />
</Provider>;
后代元素
- 引入 Consumer
import { Consumer } from './Parent'
- 使用
- Consumer 中使用一个函数,函数的参数是父元素中 value 的值,函数的返回值为节点
// Consumer 是消费者, 给 Consumer 组件传递 函数 Children ,那么该函数的参数就是 Provider 提供的 value, 该函数返回 节点
<Consumer>
{({ themeColor, changeColor }) => (
<div>
<h3 style={{ color: themeColor }}>我是子组件</h3>
<Grandson />
</div>
)}
</Consumer>
插件
样式类(css sass stylus styled-component)
- sass react 官网脚手架内置了 sass 功能只需要下载包就能使用了
- 执行
npm i node-sass sass-loader
- less react 官方脚手架并没有内置 less 需要使用 webpack 的配置给脚手架新增 less 功能
- 使用官网提供的 eject 命令暴漏配置文件
npm run eject
- 安装 less
npm i less less-loader
- 找到 webpack.config.js 配置文件
- 使用官网提供的 eject 命令暴漏配置文件
- style-components 并不需要修改配置直接使用即可
npm i styled-components
Hook
一种在函数组件内替代类组件内的 state 生命周期等的技术。
Hook 基本使用
- 导入 userState 从 react 包中
import {useState} from react
- 使用 userState 创建 state 以及修改的方法。
const [num,setNum] = useState(100)
Hook 中使用生命周期 useEffect
- useEffect 可以当作生命周期钩子,而且他表示的是初始化时以及更新的时候(componentDidMount、componentDidUpdate、componentWillUnmount)
- 给 useEffect 传递第二个参数 参数为数组,会控制该 useEffect 的触发(第二个参数可以省略,省略就是所有 state 更改 都会触发)
- 只有当数组内的数据发生改变的时候才会触发
- 无论修改组件内的哪一个 state useEffect 都会触发
- 数组内的数据并不一定写成 state
- 当数组写成空数组的时候 useEffect 只会初始化的时候触发
- 如果使用了 useEffect 的第二个参数的话,那么一定要确保数组内包含 所有外部作用域中会随时间变化并且在 effect 中使用的变量
- 注意:在第一个参数函数内如果使用了异步函数(setTimeout、setInterval、Promis)等会导致闭包,也就是内部使用的 state 可能一直是初始值,并不会发生改变
// 执行顺序 初始化 useEffect触发了➡useEffect 的参数函数的返回值触发
// 更新时候 useEffect 的参数函数的返回值触发 ➡ useEffect触发了
useEffect(() => {
console.log("useEffect触发了");
return () => {
console.log("useEffect 的参数函数的返回值触发");
};
});
useEffect 中使用 setInterval 更改属性
useEffect(() => {
// 函数组件函数本身就相当于 render 生命周期,假如不在生命周期中用定时器,页面会一直重载
// useEffect 内使用 setInterval 需要每次更新的时候销毁 setInterval 并生成新的setInterval,就是每次更新的时候 setInterval 只执行一次,然后生成新的 setInterval
// 当useEffect 内使用了 useEffect 外的变量时会出问题
const timer = setInterval(() => {
// setInterval可以替换成 setTimeout
setNum(num + 1);
}, 1000);
return () => {
if (timer) {
clearInterval(timer);
}
console.log("useEffect 的参数函数的返回值触发");
};
});
useContext
useContext 其实就是简化了 Consumer 的写法
用法
父组件
- 引入 createContext
import { createContext, useState } from "react";
- 使用 createContext
export const Theme = createContext("red")
-
export default function HookDemo2() { const [themeColor, setTehemeColor] = useState("red"); return ( <div> <h3>我是父组件{themeColor}</h3> <Theme.Provider value={{ themeColor, setTehemeColor }}> <HookDemo22 /> </Theme.Provider> </div> ); }
子组件
- 导入 them,导入 createContext
import { Theme } from "./HookDemo2"; import { useContext } from "react";
-
export default function HookDemo22() { // useContext其实就是简化了 Consumer 的写法 const { themeColor, setTehemeColor } = useContext(Theme); return ( <div> <h4>我是子组件{themeColor}</h4> <button onClick={() => setTehemeColor("pink")}>修改颜色</button> </div> ); }
userRef
- 利用 useRef 获取 dom 节点和 createRef 没区别
- 可以在函数组件中创建一个全局变量(创建好的 ref 对象在组件的整个生民周期内保持不变),可以利用 useRef 来解决 effect 函数中的闭包
import { useState, useRef, useEffect } from "react";
export default function HookDemo4() {
const [num, setNum] = useState(0);
const numRef = useRef(num);
numRef.current = num;
const add = () => {
setNum(num + 1);
};
useEffect(() => {
// 在合并没有获取到最新的 num 而是获取的最初的 num
// 因为在 useEffect 内创建了异步函数(setTimeout setInterval Promise 等),
// 那么就会形成闭包,内部函数内的变量获取的一直是最外层最初的值
// 因此利用useRef的不变性,可以解决这个问题
setTimeout(() => {
console.log("3秒后的结果为" + numRef.current);
}, 3000);
}, []);
return (
<div>
<p>{num}</p>
<button onClick={add}>num++</button>
</div>
);
}
useMemo
- 不使用 useMemo,useEffect 定义的值改变,就会重新 function 函数,也就是说计算属性没有缓存,每次都重新计算
- 我们可以使用 useMemo 来实现就按属性单独计算
- useMemo 的第一个参数必须是一个函数而且有返回值,这个返回值也会是 useMemo 的返回值
- useMemo 的第二个参数是一个数组,改数组内写的内容是依赖项,也是就是第一个参数的计算是依赖哪些数据的
const getTotal = () => {
console.log("total开始计算");
return goods.reduce((res, ele) => (res += ele.goodsPrice), 0).toFixed(2);
};
const total = useMemo(getTotal, [goods]);
自定义 Hook
就是将 hook 的逻辑封装到一个函数中,这个函数就被称为 hook 函数。
当多个组件需要用到同一种 hook 逻辑时,需要将逻辑分离成自定义 hook 然后导入使用
路由
路由基本使用
- 安装
npm i react-router-dom
- 使用 BrowserRouter(BrowserRouter 浏览器历史记录模式) 或者 HashRouter(hash 模式) 将 App 组件包裹住
import { BrowserRouter as Router } from "react-router-dom";
ReactDOM.render(
<React.StrictMode>
<Router>
<App />
</Router>
</React.StrictMode>,
document.getElementById("root")
);
- 页面展示使用
Route标签
- Route 组件是单个路由组件,必须使用 Routes 包裹
- Route 组件 接收 path element 作为 prop path 是路由地址,element 是展示的组件
- Routes 下的 route 组件只会根据地址展示一个,而且顺序是从上到下的
<Routes>
<Route path="/" element={<Home />}>
<Route element={<Post />} path="/post/:postId"></Route>
</Route>
<Route path="about" element={<About />}>
<Route path="cart" element={<Cart />}></Route>
<Route path="store" element={<Store />}></Route>
</Route>
</Routes>
- 跳转路由 Link NavLink
- 通过 NavLink 可以获取激活的状态,从而根据该状态处理样式 style className 以及 children 都可以写成函数获取 isActie
<NavLink to="/" className={({ isActive }) => (isActive ? "active" : "")} >
- 子路由
- 子路由页面展示用
Outlet
标签
<Route path="about" element={<About />}>
<Route path="cart" element={<Cart />}></Route>
<Route path="store" element={<Store />}></Route>
</Route>
- 动态路由
在 path 中写:变量名
<Route element={<Post />} path="/post/:postId"></Route>
- 获取动态路由参数
利用路由的 hook 中的 useParams 获取import { useParams } from "react-router-dom"; const { postId } = useParams();
- 默认子路由
在 Route 标签上添加 index 属性
<Route index element={<Cart />}></Route>
路由中的 hook
- 在路由组件中想要获取路由的相关信息的话
- 可以使用路由的 hook 获取
- useLocation useMatch useParams
- useParams 获取动态路由参
- useNavigate 用于跳转
{
/* replace:true替换历史记录 */
}
<button onClick={() => navigate("/", { replace: true })}>回到首页</button>;
<button onClick={() => navigate(-1)}>返回</button>;
- useLocation 当前地址(地址对象内有个 state 属性,当使用 link 或者 navigate 跳转的时候可以传递 state)
useRoutes 的使用
利用 useRoutes 来设置路由
// 创建一个单独的 routes.js文件
import { lazy, Suspense } from "react";
import About from "./views/About";
import Cart from "./views/Cart";
import Home from "./views/Home";
import Store from "./views/Store";
// 懒加载
const Post = lazy(() => import("./views/Post"));
const routers = [
{
path: "/",
element: <Home />,
children: [
{
// 默认子路由
index: true,
element: <h3>hahaha</h3>,
},
{
path: "post/:postId",
// 当需要渲染 Post 的组件的时候会先渲染 fallback的内容,直到Post组件加载完毕
element: (
<Suspense fallback={null}>
<Post />
</Suspense>
),
},
],
},
{
path: "about",
element: <About />,
children: [
{
path: "store",
element: <Store />,
},
{
path: "cart",
element: <Cart />,
},
],
},
];
export default routers;
// App 组件中
import routes from "./routes";
function App() {
const router = useRoutes(routes);
}
路由懒加载
- 路由懒加载需要 lazy, Suspense 共同使用
import { lazy, Suspense } from "react";
const Post = lazy(() => import("./views/Post"));
const routers = [
{
path: "post/:postId",
// 当需要渲染 Post 的组件的时候会先渲染 fallback的内容,直到Post组件加载完毕
element: (
<Suspense fallback={null}>
<Post />
</Suspense>
),
},
];
路由守卫
- element 中添加一个组件,传递 children prop
const routers = [
{
path: "/",
element: (
<RequireAuth>
<Home />
</RequireAuth>
),
},
];
- 在 RequireAuth 中进行判断
import { message } from "antd";
import { useEffect } from "react";
import { Navigate } from "react-router-dom";
const NavigateTip = () => {
useEffect(() => {
message.error("没有登录请登录");
});
return <Navigate to="/login" replace />;
};
export default function RequireAuth({ children }) {
const info = sessionStorage.getItem("info");
return info ? children : <NavigateTip />;
}
redux
独立的状态管理工具。在 react 中使用的话,还需要借助 react-redux(或者 Redux Toolkit)使用
-
安装 redux
npm i redux
-
安装 react-redux
npm i react-redux
-
创建 createStore(reducer)
- createStore 方法
// creatStore是初始化 store 的方法,传递 reducer函数 作为参数,createStore 执行的时候自动 reducer 函数,并且将reducer函数的返回值制作成 store 数据 const store = createStore(rootReducer, enhancer); export default store;
- reducer 函数(初始化 store 以及添加修改数据的行为)
// reducer函数 // 需要定义两个参数,第一个参数state可用来初始化store数据 // 第二个参数 action 是一个对象存储了type属性。这个action是一个行为名称 // 这个函数必须设置返回值,返回的值是 store 的数据,默认要返回第一个参数 const rootReducer = (state = { counter: 0 }, action) => { const { type } = action; switch (type) { case "increment": return { counter: state.counter + 1, }; default: return state; } };
-
使用 需要使用 react-redux
-
先用 Provider 提供 store 给 App 组件
import { Provider } from "react-redux"; import store from "./store/index"; ReactDOM.render( <React.StrictMode> <Provider store={store}> <App /> </Provider> </React.StrictMode>, document.getElementById("root") );
-
在组件中使用 connect 或者 useSelector 动态获取 store 数据
import { useSelector, useDispatch } from "react-redux"; const counter = useSelector((state) => state.counter);
- connect 方法
import "./App.css"; import store from "./store/index"; import { connect } from "react-redux"; function App({ counter }) { return ( <div className="App"> <h3>{counter}</h3> </div> ); } const mapStateToProps = (state) => ({ counter: state.counter, }); export default connect(mapStateToProps)(App);
-
修改的话使用 store.dispatch 发对应 action 或者直接使用 useDispatch 获取 dispatch 来发 action
import { useSelector, useDispatch } from "react-redux"; const dispatch = useDispatch(); dispatch({ type: "increment" });
-
-
处理异步操作,需要使用 redux-thunk
- 安装
npm i redux-thunk
- 使用 applyMiddleware 来扩展 thunk
- import { applyMiddleware } from “redux”;
- const store = createStore(rootReducer, applyMiddleware(thunk));
- 在 action 创建函数中写成 thunk 函数
export const initial = () => (dispatch) => { setTimeout(() => { dispatch({ type: "initial", newCounter: 100, }); }, 1000); };
- 安装
不可变性
Redux 期望所有状态的更新都是不可变性的
toolkit
- 安装
npm install @reduxjs/toolkit
或者npx create-react-app my-app --template redux
创建一个模板 - 创建切片
- 利用 createSlice 创建切片
// 数据初始值
const initialState = {
value: 0,
};
// 创建切片
const counterSlice = createSlice({
name: "counter",
initialState,
// reducers 动作处理 写法可以对比之前的 switch 判断,写成对象
reducers: {
increment: (state) => {
state.value++;
},
},
});
// 导出action
export const [increment] = createSlice.actions;
// 导出切片
export default counterSlice;
- 导入切片
利用 configureStore
import { configureStore } from "@reduxjs/toolkit";
import counter from "./slices/counter";
export const store = configureStore({
reducer: {
[todos.name]: todos.reducer,
},
});
toolkit 处理异步
方法一
// 要处理异步的action可以直接创建
// 异步action内层函数接收dispatch以及getState函数作为参数
// dispatch用来发action
// getState用于获取state数据
export const fetchCounter = () => (dispatch, getState) => {
console.log(getState());
setTimeout(() => {
dispatch(change(10));
}, 1000);
};
方法二 利用 createAsyncThunk
好处: dispatch 调用后可以加 then
// 利用 createAsyncThunk 创建函数
// createAsyncThunk 返回值是一个 promise
// 当组件dispatch(函数()) promise 执行
// 执行的时候 extraReducers 会监听 promise的状态,去做对应的处理
// createAsyncThunk的第二个参数函数的返回值会被当作 action 的payload
export const fetchCounter = createAsyncThunk(
"counter/fetchCounter",
async () => {
const res = await new Promise((resolve, rejected) => {
setTimeout(() => {
resolve(1000);
}, 1000);
});
return res;
}
);
// 在切片中使用 extraReducers配置项
const counterSlice = createSlice({
name: "counter",
initialState,
reducers: {
increment: (state) => {
state.value++;
},
change: (state, action) => {
state.value = action.payload;
},
},
// extraReducers用来处理 createAsyncThunk 创建的异步thunk
// fulfilled是异步的状态 pending rejected
extraReducers: (builder) => {
builder.addCase(fetchCounter.fulfilled, (state, action) => {
state.value = action.payload;
});
},
});
计算属性 createSelector
import { createSelector } from "@reduxjs/toolkit";
const selectTodos = (state) => state.todos.value;
const selectFilter = (state) => state.filter.value;
// 根据 filter 展示todos
export const selectFilterTodos = createSelector(
selectTodos,
selectFilter,
(todos, filter) => {
return todos.filter((todo) =>
filter === "all"
? true
: filter === "active"
? !todo.isComputed
: todo.isComputed
);
}
);