react(包括react-router、redux、reduxToolkit)

react

jsx 语法

js 内嵌入 的 html 标签 就是 jsx 语法。 html 标签内嵌入 js。 jsx 内写 js 直接使用 {}。 jsx 内标签的属性有几个需要特殊记忆 1. class 写成 className 2. lable 内的 for 写成 htmlFor 3. 自定义属性名写成小驼峰 4. 标签内无内容写成单闭合

返回多个根节点

  1. 默认只能返回一个根节点
  2. 可以结束 Fragments 返回多个节点 <> 就是空标签 不会渲染任何标签 空标签是简写

组件

  1. 函数组件 根据 props 展示 html 内容
  2. 类组件 做功能
  3. 区别:
  • 有无 state 类组件有
  • 有无 声明周期 类组件有
  • 有了 hook 后 函数组件内也可以使用 state 和声明周期

props

  1. 功能:组件复用的时候父组件给子组件传递数据
  2. 注意: props 为只读的不可更改
函数组件内使用 props
  1. 函数组件的函数内默认接收一个参数交 props,参数 props 内存储的就是父组件传递的 props 对象,props 为只读的不可更改
类组件内使用 props
  1. render 函数默认会触发
  2. 组件的 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. 事件对象获取
  1. 没有传递事件参数可以直接在形参中获
  2. 传递事件参数
<button
  onClick={(e) => {
    login_test("哈哈哈哈", e);
  }}
>
  事件传参
</button>

类组件中的 this 问题

  1. 非声明周期函数(render 等)内想要使用 this 的话需要创建成箭头函数
export default class Button extends Component {
  handleClick = () => {
    console.log("我是按钮点击事件");
    console.log(this.props);
  };
}
  1. 利用 bind 解决
<button onClick={this.handleClick.bind(this)}>{text}</button>

state

状态变页面变,页面变化需要 state 控制

更改 state 的值
  1. 修改 state 不能直接修改 需要使用 setState 方法修改 这个方法是 Component 类自带的,所以所有组件都可以使用

  2. 使用 setState 的时候传递一个对象,对象内的属性写的就是要修改的具体的 state

  3. 切记不能直接修改state

  4. setState 何时同步何时异步?
    由 React 控制的事件处理程序,以及生命周期函数调用 setState 不会同步更新 state 。
    React 控制之外的事件中调用 setState 是同步更新的。比如原生 js 绑定的事件,setTimeout/setInterval/async await 等。

  5. 更改后想要获取新的值,加一个定时器

state = {
  count: 100,
};
// count++
this.setState({
  count: this.state.count + 1, //不能写++ ++是更改并赋值操作直接就改了state
});
  1. 更改数组或这对象的时候不能只更改其中一个的值,需要将整个对象或者数组重新赋值。可以利用展开运算符简化
state = {
  user: {
    userName: "xm",
    userAge: 18,
  },
};
changeAge = () => {
  /*  this.setState({
      user: {
        userName: "xm",
        userAge: 20,
      },
    }); */
  this.setState({
    user: { ...this.state.user, userAge: 20 },
  });
};

组件消失出现

  1. 样式消失
<div className="box" style={{ display: show ? "block" : "none" }}></div>
  1. 真正消失
{
  show ? <div className="box"></div> : "";
}
// 当在 html 内写 undefined null '' false 的时候不会渲染任何内容
{
  show && <div className="box"></div>;
}

列表渲染

  1. react 的 jsx 内可以直接将数组内的值渲染到页面上
  2. 列表渲染 其实就是将数组数据变为 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 生命周期

  1. 生命周期函数不写成箭头函数也可以获取 this
1.挂载 初始的渲染阶段 执行顺序从上到下
  1. constructor() 类的初始化,1.初始化 state 2.为事件处理函数绑定实例(es6 的箭头函数事件无需绑定实例) 用的很少
  • 它接受两个参数:props 和 context,当想在函数内部使用这两个参数时,需使用 super()传入这两个参数。
  • 注意:只要使用了 constructor()就必须写 super(),否则会导致 this 指向错误。
  1. static getDerivedStateFromProps() 从 props 中获取 state 这个生命周期的功能实际上就是将传入的 props 映射到 state 上面
  • static 生命的方法是 类的私有方法,只能在类内使用,类的实例无法调用
  1. render() 必须返回页面内容,主要作用提供虚拟 dom
  2. componentDidMount() 页面挂载完毕,组件的 html 渲染到页面上. 在这里面可以直接调用 setState,所以在这里发请求拿数据
2.更新 当组件的 state 或者 props 发生改变的时候就会触发组件的更新
  1. static getDerivedStateFromProps() 用的很少
  2. shouldComponentUpdate() 在更新的时候可以使用该函数阻止更新 必须返回一个布尔值,该函数接收两个参数 新的 props 新的 state
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate() 组件更新 state 或者 props 完毕,页面渲染完毕。在这个函数内也可以修改 state,但是必须写一个条件句去修改,否则会无限循环
3.卸载
  1. componentWillUnmount()

表单

受控组件
  1. 在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()来更新。
  2. 需要绑定 state 值和 onChange 事件 onChange 事件是输入了就触发,并不是失去焦点才触发
  3. 可以设置一个公共的更改表单的方法,根据 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}
/>;
  1. 多选框
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 />
非受控组件
  1. 非受控组件 不使用 state。 初始展示效果的话,需要使用 defaultValue 或者 defaultChecked 等展示,不直接使用 value 等,获取该元素的状态的话,使用 ref 获取

ref 的使用

  1. 先用 createRef 创建
  2. 在元素上使用
  3. 获取元素实例对象需要使用 curent 方法
  4. 在元素身上获取元素对象,在组件身上获取组件实例对象
import { Component, createRef } from "react";
checkBox = createRef();
<input type="checkbox" defaultChecked={true} ref={this.checkBox} />;
// 获取组件实例
checkBox.current;

解析 html

  1. 想要解析 html 字符串 需要使用 dangerouslySetInnerHTML 属性,该属性的值是一个对象,对象的__html 属性是 html 字符串的话会被解析到该元素内
<div dangerouslySetInnerHTML={{ __html: content }}></div>

children prop

当父组件传递的内容在子组件双标签之间的话,那么子组件或使用 children prop 来接收

  1. 当组件自己无法确定自己的部分内容的展示,需要父组件提供
  2. 此时我们就可以使用父组件 传递得 childern prop 来实现
  3. 比如 对话框组件
// 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 类型检查

使用方法
  1. 导入 prop-types import PropTypes from "prop-types"; PropTypes 这个包来处理类型检查,这个包已经默认下载好了,无需自己下载
  2. 类型检查 类.propTypes = {}
  3. isRequired 必传
PropTypesDemo.propTypes = {
  title: PropTypes.string,
  description: PropTypes.string.isRequired, //必传
};
设置默认值
  1. 类.defaultProps = {}
PropTypesDemo.defaultProps = {
  title: "默认标题",
};

context

context 用于父元素向后代元素传递数据

用法
父组件
  1. 引入 createContext import { createContext } from 'react'
  2. 根据 createContext 创建一个 context 上下文数据,并赋值初始值 创建好的 context 内有两个组件 Provider 提供者, Consumer 消费者 export const {Provider, Consumer} = createContext(’#00b3d4’);
  3. 父元素向后代提供
state = {
  themeColor: "blue",
};
 changeColor = (color) => {
    this.setState({
    themeColor: color
})
// changeColor为更改属性函数
<Provider value={{ themeColor, changeColor: this.changeColor }}>
  {/* 利用 Provider 组件将 value 的值传递给 son 组件以及后代组件 */}
  <Son />
</Provider>;
后代元素
  1. 引入 Consumer import { Consumer } from './Parent'
  2. 使用
  • 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 配置文件
  • style-components 并不需要修改配置直接使用即可npm i styled-components

Hook

一种在函数组件内替代类组件内的 state 生命周期等的技术。

Hook 基本使用
  • 导入 userState 从 react 包中 import {useState} from react
  • 使用 userState 创建 state 以及修改的方法。 const [num,setNum] = useState(100)
Hook 中使用生命周期 useEffect
  1. useEffect 可以当作生命周期钩子,而且他表示的是初始化时以及更新的时候(componentDidMount、componentDidUpdate、componentWillUnmount)
  2. 给 useEffect 传递第二个参数 参数为数组,会控制该 useEffect 的触发(第二个参数可以省略,省略就是所有 state 更改 都会触发)
  3. 只有当数组内的数据发生改变的时候才会触发
  4. 无论修改组件内的哪一个 state useEffect 都会触发
  5. 数组内的数据并不一定写成 state
  6. 当数组写成空数组的时候 useEffect 只会初始化的时候触发
  7. 如果使用了 useEffect 的第二个参数的话,那么一定要确保数组内包含 所有外部作用域中会随时间变化并且在 effect 中使用的变量
  8. 注意:在第一个参数函数内如果使用了异步函数(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 的写法

用法
父组件
  1. 引入 createContext import { createContext, useState } from "react";
  2. 使用 createContext export const Theme = createContext("red")
  3. export default function HookDemo2() {
      const [themeColor, setTehemeColor] = useState("red");
      return (
        <div>
          <h3>我是父组件{themeColor}</h3>
          <Theme.Provider value={{ themeColor, setTehemeColor }}>
            <HookDemo22 />
          </Theme.Provider>
        </div>
      );
    }
    
子组件
  1. 导入 them,导入 createContext import { Theme } from "./HookDemo2"; import { useContext } from "react";
  2. export default function HookDemo22() {
      // useContext其实就是简化了 Consumer 的写法
      const { themeColor, setTehemeColor } = useContext(Theme);
      return (
        <div>
          <h4>我是子组件{themeColor}</h4>
          <button onClick={() => setTehemeColor("pink")}>修改颜色</button>
        </div>
      );
    }
    
userRef
  1. 利用 useRef 获取 dom 节点和 createRef 没区别
  2. 可以在函数组件中创建一个全局变量(创建好的 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
  1. 不使用 useMemo,useEffect 定义的值改变,就会重新 function 函数,也就是说计算属性没有缓存,每次都重新计算
  2. 我们可以使用 useMemo 来实现就按属性单独计算
  3. useMemo 的第一个参数必须是一个函数而且有返回值,这个返回值也会是 useMemo 的返回值
  4. 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 然后导入使用

路由

路由基本使用
  1. 安装 npm i react-router-dom
  2. 使用 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")
);
  1. 页面展示使用 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>
  1. 跳转路由 Link NavLink
  • 通过 NavLink 可以获取激活的状态,从而根据该状态处理样式 style className 以及 children 都可以写成函数获取 isActie
<NavLink to="/" className={({ isActive }) => (isActive ? "active" : "")} >
  1. 子路由
  • 子路由页面展示用 Outlet标签
<Route path="about" element={<About />}>
  <Route path="cart" element={<Cart />}></Route>
  <Route path="store" element={<Store />}></Route>
</Route>
  1. 动态路由
    在 path 中写:变量名
<Route element={<Post />} path="/post/:postId"></Route>
  1. 获取动态路由参数
    利用路由的 hook 中的 useParams 获取
    import { useParams } from "react-router-dom";
    const { postId } = useParams();
    
  2. 默认子路由
    在 Route 标签上添加 index 属性
<Route index element={<Cart />}></Route>
路由中的 hook
  1. 在路由组件中想要获取路由的相关信息的话
  2. 可以使用路由的 hook 获取
  3. useLocation useMatch useParams
  4. useParams 获取动态路由参
  5. useNavigate 用于跳转
{
  /* replace:true替换历史记录 */
}
<button onClick={() => navigate("/", { replace: true })}>回到首页</button>;
<button onClick={() => navigate(-1)}>返回</button>;
  1. 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);
}
路由懒加载
  1. 路由懒加载需要 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>
    ),
  },
];
路由守卫
  1. element 中添加一个组件,传递 children prop
const routers = [
  {
    path: "/",
    element: (
      <RequireAuth>
        <Home />
      </RequireAuth>
    ),
  },
];
  1. 在 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

  1. 安装 npm install @reduxjs/toolkit 或者 npx create-react-app my-app --template redux创建一个模板
  2. 创建切片
  • 利用 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
    );
  }
);

你可能感兴趣的:(react.js,javascript,前端)