0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React

Last updated at Posted at 2025-03-20

useState 遍历渲染 绑定事件

一:变量和方法的定义,写法

const message = "this is message"
function App() {
  return(
   <div>
     <h1>this is title</h1>
     {message}
   </div>
  )
}

1.变量

变量被定义在组件之外:变量可以在整个文件里使用
变量被定义在组件之内:变量只可以在组件内部使用

const message = "this is message"

2.方法

一般方法写在组件内部
getMessage 是一个方法,返回一段文本,并在 return 语句中调用它。

function App() {
  // 方法的声明
  const getMessage = () => {
    return "this is a function message";
  };

  return (
    <div>
      <h1>this is title</h1>
      {getMessage()}   // 方法的调用
    </div>
  );
}

3.return语句

return语句中写的是HTML,渲染到页面上的内容

4.顺序

一般来说,React 组件内部的代码组织顺序是:变量 方法 return语句(HTML 代码)

5.{}作用

通过{}识别表达式,比如变量,函数调用,方法调用等
注意:if switch语句,变量声明属于语句,不是表达式,不能出现在{}中

// ①使用引号传递字符串
{"abc"}
// ②使用js变量
const count = 100
{count}
// ③函数调用和方法调用
function getName(){
  return "jack"
}
{getName()}

{new Date().getDate()}
// ④使用js对象
<div style={{color:"red"}}>this is div</div>

二:遍历列表 map

※:加上key(id) item:表示遍历出来的每一项

function App() {
  const list = [
    { id: 1001, name: "Vue" },
    { id: 1002, name: "React" },
    { id: 1003, name: "Angular" }
  ];

  return (
    <ul>
      {list.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}
export default App;

三:条件渲染

1.单个元素

通过&&控制单个元素的显示与隐藏

2.两个元素

三元运算符

const isLogin = true
// 单个元素
{isLogin && <span>this is span</span>}
// 两个元素
{isLogin?<span>jack</span>:<span>loading</span>}

3.复杂条件渲染

自定义函数+if判断语句

// 定义文章类型
const articleType = 3  // 有三种情况 0  1  3

// 定义核心函数:根据文章类型返回不同的jsx模板
function getArticleTem(){
  if(articleType === 0){
    return <div>无图文章</div>
  }else if(articleType === 1){
    return <div>单图文章</div>
  }else{
    return <div>三图文章</div>
  }
}

function App(){
  return (
    <div className="App">
    // 调用函数渲染不同的模板
    {getArticleTem()}
    </div>
  )
}

四:事件绑定

1.基础绑定

on+事件名称={事件处理程序} 遵循驼峰命名
绑定点击事件:onClick={clickHandler}

function App(){
  const clickHandler = ()=>{
     console.log("button按钮点击了")
  }
  return(
    <button onClick={clickHandler}></button>
  )
}

2.事件参数e

function App(){
  const clickHandler = (e)=>{
     console.log("button按钮点击了",e)
  }
  return <button onClick={clickHandler}>click me</button>  
}

3.传递自定义参数

事件绑定的位置改造成箭头函数的写法,在执行clickHandler实际处理业务函数的时候传递实参
onClick 事件本身并不会自动传递参数。所以,通过箭头函数,能够将自定义的参数 "jack" 传递给事件处理函数。

function App(){
  const clickHandler = (name)=>{
     console.log("button按钮点击了",name)
  }
  return <button onClick={() => clickHandler("jack")}>click me</button>  
}

4.既要传递自定义参数,而且还要事件对象e

function App(){
  const clickHandler = (name,e)=>{
     console.log("button按钮点击了",name,e)
  }
  return <button onClick={(e) => clickHandler("jack",e)}>click me</button>  
}

五:组件基础使用

一个组件就是首字母大写的函数,内部存放了组件的逻辑和视图ui,渲染组件只需要把组件当成标签书写即可

// 1.定义组件
function Button(){
   // 组件内部逻辑
   return <button>click me</button>
}

// 2.使用组件
function App(){
  return (
     <div>
     // 自闭和
     <Button />
     </div>
  )
}

六:useState

1.概念

useState是用来给组件添加状态变量的,状态变量一旦发生变化组件的视图ui也会跟着变化

// 导入
import {useState} from "react"
// 1.useState是一个函数,返回值是一个数组
// 2.数组中的第一个参数是状态变量,第二个参数是set函数用来修改状态变量
// 3.useState的参数将作为count的初始值
const [count, setCount] = useState(0)
// useState实现一个计数器按钮
import {useState} from "react"
function App(){
// 1.调用useState添加一个状态变量
// count:状态变量  setCount:修改状态变量的方法
const [count, setCount] = useState(0)

// 2.点击事件回调
const handleClick =()=>{
  // 作用:用传入的新值修改count,重新使用新的count去渲染ui
  setCountcount+1
}
  return (
     <div className="App">
     // 使用组件
     <button onClick={handleClick}>{count}</button>
     </div>
  )
}

2.useState修改状态的规则

状态是只读的,只能替换不能修改
使用新值替换老值,使用新对象替换老对象
修改对象状态:传给set方法一个全新的对象来进行修改
使用展开运算符 ...form 来保留 form 对象中的其他属性,避免覆盖整个对象。

const [form, setForm] = useState({
  name:"jack",
})

const handleChangeName = ()=>{
  setForm({
    ...form,
    name:"john",
  })
}

七:基础样式控制-class类名控制

className="foo"

①index.css文件

.foo{
   color:blue;
}

②App.js文件

// 导入样式
import "./index.css"

function App(){
  return (
     <div className="App">
     // 自闭和
     <span className="foo">this is class foo</span>
     </div>
  )
}

八:classnames优化类名控制

classnames用来根据条件动态地合并类名。

// 安装方式
npm install classnames

image.png

// 使用了classnames库来动态设置React组件中的className
import classNames from "classnames"
 className={classNames("nav-item",{active:type === item.type})}

九:案例-评论列表

image.png

1.渲染评论列表--map方法遍历

※:只要数据会影响到视图的变化,都应该用useState去做维护
※:状态变量必须有一个初始值。commentList的初始值useState(list)

// 1.使用useState维护评论列表list
const[commentList,setCommentList]=useState(list)
// 2.遍历渲染评论项
{commentList.map(item=> (
   <div key={item.rpid}>评论项</div>
))}
// 3.修饰细节 头像 用户名 评论内容 评论时间 评论数量
{item.user.avatar}
{item.user.uname}
{item.content}
{item.ctime}
{item.like}

2.删除自己的评论

只有自己的评论才显示删除按钮,点击删除按钮,删除评论,列表中不再显示
思路:①条件渲染判断是否是自己的评论
②通过id去做匹配,过滤(用户id和评论列表中id)
⭐filter函数返回一个新数组,不更改老数组。类似于一个漏斗,满足条件的留下,不满足条件的漏下去。比如:一共有五条评论,id分别为12345,用户id和评论区id都为3,那么就把3漏下去,把1245作为一个新的数组返回。保留所有rpid !== id 的评论(即删除 rpid === id 的评论)
⭐所有id都是评论ID(rpid),并不是用户 ID(uid)。
⭐className="delete-btn" 用于为这个 元素添加 CSS 样式

// 条件:user.id=== item.user.id
// 删除功能
const handleDel = (id)=>{
  console.log(id)
  // 对commentList做过滤处理
  setCommentList(commentList.filter(item=> item.rpid !== id ))
}
{user.uid=== item.user.uid && 
  <span className="delete-btn" onClick={()=> handleDel(item.rpid)}>
  删除
  </span>}

3.实现渲染导航Tab和高亮

点击哪个tab项,哪个做高亮处理
点击谁就把谁的标识记录下来,然后和遍历时的每一项标识做匹配,谁匹配到就设置负责高亮的类名

const [type,setType] = useState("hot")
const handleTabChange =(type) =>{
  console.log(type)
  setType(type)
}
const tabs = [
    {type:"hot",text:"最热"},
    {type:"time",text:"最新"},
]
// 高亮类名 active
{tabs.map(item=> 
    <span 
       key={item.type} 
       onClick={()=>handleTabChange(item.type)} 
       className={"nav-item ${type === item.type && "active"}"}>
       {item.text}
    </span>)}

父子通信 useEffect

一:受控表单绑定

通过react状态去控制Input框的状态
image.png
监听输入框内容变化,每当用户输入时,e.target.value(输入框的值)会更新 value,从而实现输入框内容与状态同步。

import {useState} from "react"
// 1.准备一个React状态值
const [value,setValue] = useState("")
// 2.通过value属性绑定状态,通过onChange属性绑定状态同步的函数
// 通过事件参数e拿到输入框的最新的值,反向修改到react状态身上
<input
  type="text"
  value={value}
  onChange={(e) => setValue(e.target.value)}
/>

二:react中获取dom(使用useRef)

使用useRef获取input输入框的DOM对象
①1.useRef生成ref对象,并与JSX绑定
②2.dom可用时,通过inputRef.current拿到dom对象

import {useRef} from "react";

function App(){
   // 1. 创建一个 ref 对象,并初始化为 null
   const inputRef = useRef(null)

   // 2. 定义一个函数,点击按钮时获取 input 的 DOM 对象
   const showDom =()=>{
      console.log(inputRef.current)  // 输出 input 的 DOM 对象
   }
   return(
     <div>
        {/* 3. 绑定 ref 到 input 元素 */}
       <input type="text" ref={inputRef} />
        {/* 4. 点击按钮时执行 showDom 函数 */}
       <button onClick={showDom}>获取dom</button>
     </div>
   )
}

三:案例-发表评论

1.核心功能实现

①用数控绑定方式,可以把用户输入的东西,收集到content里面
②点击时发布评论,替换content字段,收集到的数据是什么就绑定什么
③延续数据修改不可变的方式,原本的所有数据都展开,增加一条自己的数据
※需求:获取评论内容,点击发布按钮发布评论
⭐setCommentList([...commentList, newComment]):
...commentList 复制已有的评论列表。
{...} 创建一个新评论对象,并添加到列表中。
setCommentList() 更新 commentList 状态,让页面重新渲染,显示新评论。

 // 发表评论
const [content,setContent] = useState("")
const handPublish =()=>{
  setCommentList([
      ...commentList,
      {
         rpid:100,
         user:{
            uid:"30009257",
            avatar,
            uname:"黑马前端",
         },
         content:content,
         ctime:"10-19 09:00",
         like:66,
         }
  ])
}
// 评论框
<textarea
    className="reply-box-textarea"
    placeholder="发一条友善的评论"
    value={content}
    onChange={(e)=>setContent(e.target.value)}
/>
// 发布按钮
<div className="reply-box-send">
   <div className="send-text" onClick={handPublish}>发布</div>
</div>   

2.id和时间处理

需求:rpid要求一个唯一的随机数id(uuid)
ctime要求以当前时间为标准,生成固定格式(dayjs)

// 安装uuid
npm install uuid
// 使用方式
 import {v4 as uuidv4} from "uuid";
 uuidv4();

 // 安装Day.js
 npm install dayjs
 // 使用方式
 import dayjs from "dayjs"
 Format
{
import {v4 as uuidV4} from "uuid"
import dayjs from "dayjs"
         rpid:uuidV4(), //随机id
         user:{
            uid:"30009257",
            avatar,
            uname:"黑马前端",
         },
         content:content,
         ctime:dayjs(new Date()).format("MM-DD hh:mm"),// 格式化 月-日 时:分
         like:66,
         }

3.清空内容和聚焦实现

image.png
需求:清空内容-把控制input框的value状态设置为空串
重新聚焦-拿到input的dom元素,调用focus方法
inputRef.current.focus() 让输入框自动获取焦点

// 1.清空输入框内容
setContent("")
// 2.重新聚焦 dom(useRef) -focus
const inputRef = useRef(null)
inputRef.current.focus()
// 评论框
<textarea
    className="reply-box-textarea"
    placeholder="发一条友善的评论"
    ref={inputRef}
    value={content}
    onChange={(e)=>setContent(e.target.value)}
/>    

四:父子组件通信

组件之间的数据传递

1.父传子基础实现

步骤:①父组件传递数据-在子组件标签上绑定属性
②子组件接收数据-子组件通过props参数接收数据
props:包含着父组件传递过来的所有数据
父组件App通过props向子组件Son传递数据,然后子组件Son在渲染时接收并显示这个数据。

function Son(props){
  console.log(props)
  return <div> this is son,{props.name}</div>
}

function App(){
   const  name = "this is app name"
   return(
      <div>
        <Son name = {name}/>  {/* 传递name变量给Son组件 */}
      </div>
   )
} 

2.props说明

可以传递任意的数据:数字,字符串,布尔值,数组,对象,函数,jsx
props是只读对象,子组件只能读取props中的数据,不能直接进行修改,父组件的数据只能有父组件修改

3.父传子children说明

父组件通过children传递子元素给Son组件,然后Son组件可以通过 props.children渲染这个子元素。
props.children:Son组件的子元素

function Son(props){
  console.log(props)
  // props.children在这里是<span> this is span </span>
  return <div> this is son,{props.children}</div>
}

function App(){
   return(
      <div>
        <Son>
          <span> this is span</span>
        </Son>
      </div>
   )
} 

4.子传父实现

在子组件中调用父组件中的函数并传递实参
父组件通过props传递一个函数getMsg给子组件,然后子组件在点击按钮时调用这个函数,并把自己的数据sonMsg传回给父组件。

function Son({onGetSonMsg}){
   // Son组件中的数据
   const sonMsg = "this is son msg"
   return(
      <div>
        this is Son
        <button onClick ={()=>onGetSonMsg(sonMsg)}>sendMsg</button>
      </div>
   ) 
}

function App(){
  const [msg,setMsg] = useState("")
  const getMsg = (msg)=>{
    console.log(msg)
    setMsg(msg)
  }
   return(
      <div>
          this is App,{msg}
         <Son onGetSonMsg = {getMsg}/>
      </div>
   )
}    

五:useEffect

1.概念理解和基础使用

用来执行一些“自动触发的操作”,而不是等到用户点击按钮、输入内容等事件才执行。可以想象成一个“自动运行的代码块”:当组件加载(渲染)时,就会自动执useEffect里的代码,当某些数据变化时(比如 state 发生变化),useEffect也可以自动执行。
useEffect 不需要用户手动触发(比如点击按钮),它会在页面加载或状态变化时自动执行。比如:页面一打开就执行代码,或某个状态变化后自动执行代码
你只管写在useEffect里,React会帮你找合适的时机自动执行!

useEffect(() =>{},[])
// 参数1是一个函数,在函数内部可以放置要执行的操作
// 参数2是一个数组,在数组里放置依赖项,不同依赖项会影响第一个参数函数的执行,
//                 当是一个空数组的时候,只会在组件组件首次渲染时执行一次
    

⭐作用
①首次进入页面后,初始化操作
②监视某个值的变化
③退出页面后,执行收尾操作

2.不同依赖项说明

①空数组依赖:只在初始渲染时执行一次

import { useEffect, useState } from "react";

function App() {
  // 2.传入空数组依赖  初始执行一次
  useEffect(()=>{
    console.log("副作用函数执行了")
  },[])
  return (
    <div>
         this is App
         <button onClick ={()=>setCount(count +1)}>+{count}</button>
    </div>
  )
}
export default App; 

②添加特定依赖项:组件初始渲染+特性依赖项变化时执行

import { useEffect, useState } from "react";

function App() {
  const [count,setCount] = useState(0)
  // 3.传入特定依赖项  初始 + 依赖项变化时执行
  useEffect(()=>{
    console.log("副作用函数执行了")
  },[count])
  return (
    <div>
         this is App
         <button onClick ={()=>setCount(count +1)}>+{count}</button>
    </div>
  )
}
export default App;    

3.清除副作用

在useEffect里,“副作用”指的是组件渲染时需要执行的额外操作,比如:
发送网络请求,操作 DOM,订阅事件,定时器。这些操作不是组件渲染UI所必需的,但又是程序运行时需要的,所以叫“副作用”。
React提供了清理副作用的方法,就是在useEffect里返回一个函数,这个函数会在组件卸载或useEffect重新执行时运行,负责清除不需要的副作用。
useEffect是“开灯”,但如果灯一直亮着没人关,就会浪费电。所以React允许你在组件卸载时“关灯”,就是清除副作用!

 useEffect(() =>{
    // 实现副作用操作逻辑
    return()=>{
    // 清除副作用逻辑
    }
 },[])  

在Son组件渲染时开启一个定时器,卸载时清除这个定时器
①Son 组件在渲染时启动一个定时器,每隔 1 秒输出 "定时器执行中"。
②App 组件包含一个 show 状态,控制 Son 组件是否显示。
③点击按钮 setShow(false) 会卸载 Son 组件,同时 useEffect 清理定时器,防止定时器继续运行。

import { useEffect, useState } from "react";

function Son(){
  // 1.渲染时开启一个定时器
  useEffect(()=>{
     const timer = setInterval(()=>{
      console.log("定时器执行中")
     },1000)

     return () =>{
      // 清除副作用 组件卸载时
      clearInterval(timer)
     }
  },[])
  return <div>this is son</div>
}

function App() {
  // 通过条件渲染模拟组件卸载
  const [show,setShow] = useState(true)
  return (
    <div>
         {show && <Son />}
         <button onClick ={()=>setShow(false)}>卸载Son组件</button>
    </div>
  )
}
export default App;   

六:自定义hook实现

useToggle是一个自定义Hook,用于管理 true/false 状态。
好处是让toggle逻辑可以复用,多个组件可以直接使用。
按钮点击时,toggle() 切换 value,从而控制

显示/隐藏。
import { useEffect, useState } from "react";

// 封装自定义hook

// 1.声明一个以use打头的函数
// 2.在函数体内封装可复用的逻辑 只要是可复用的逻辑
// 3.把组件中用到的状态或者回调return出去 以对象或者数组
// 4.在哪个组件中要用到这个逻辑,就执行这个函数,解构出来状态和回调进行使用
function useToggle(){
  // 可复用的逻辑代码
  const [value,setValue] = useState(true)
  const toggle = ()=>setValue(!value)

  // 哪些状态和回调函数需要在其他组件中使用 return
  return{
    value,
    toggle
  }
}

function App() {
  const {value, toggle} = useToggle()
  return (
    <div>
        {value && <div>this is div</div>}
         <button onClick ={toggle}>toggle</button>
    </div>
  )
}
export default App;    

七:reacthooks使用规则说明

1.只能在组件中或者其他自定义hook函数中调用
2.只能在组件的顶层调用,不能嵌套在if for其他函数中

Redux

一:Redux

在React里,组件之间传递数据通常要一层层用props传递,会变得很麻烦。Redux通过 集中管理数据,让所有组件都可以直接从Store获取数据,避免了繁琐的传递。
Store(存储):存储整个应用的状态数据(state),并且只能通过dispatch(action) 来修改。

使用步骤

①定义一个reducer函数
reducer是一个函数,用于接收旧的state和action,并返回新的state
它根据action生成新的state,不能直接修改原来的state,必须返回一个新对象
在Redux中,所有的状态更新都必须通过Reducer进行,而不能直接修改state
组件触发dispatch(action),让Reducer更新state
reducer函数的参数必须有两个,分别是:
state:当前的状态(需要它来存储和管理数据)
action:要执行的操作(需要通过它决定如何修改数据)包含type(操作类型和 payload(数据)

// 计数器(加1、减1)
function reducer(state = { count: 0 }, action) {
  if (action.type === "INCREMENT") {
    return { count: state.count + 1 };
  }
  if (action.type === "DECREMENT") {
    return { count: state.count - 1 };
  }
  return state; // 没有匹配的action,返回原来的state
}

②创建store实例
store就是Redux的数据中心,它存储所有的状态。
createStore(reducer) 用来创建 store

import { createStore } from "redux";

const store = createStore(reducer);

③订阅store数据变化(subscribe)
subscribe作用是:当Redux里的数据变化时,就会自动触发这个函数。
每次store里的数据有变化,就会执行subscribe里的回调函数
store.getState() 获取最新的数据并打印出来

store.subscribe(() => {
  console.log("状态更新了:", store.getState());
});

④ 提交action修改数据(dispatch)
dispatch(action)触发reducer 进行数据修改
dispatch需要传一个action对象,type表示要执行的操作。
这时reducer会修改状态,然后store更新数据,触发subscribe里的回调

store.dispatch({ type: "INCREMENT" }); // 让 count +1
store.dispatch({ type: "INCREMENT" }); // 再加 1
store.dispatch({ type: "DECREMENT" }); // 让 count -1

⑤获取最新的state
store.getState()可以获取当前store里的最新数据
每次dispatch执行后,可以用getState()查看数据是否正确更新了

console.log("最终 count:", store.getState().count);

⭐完整代码

import { createStore } from "redux";

// 1. 定义 reducer(管理状态)
function reducer(state = { count: 0 }, action) {
  if (action.type === "INCREMENT") {
    return { count: state.count + 1 };
  }
  if (action.type === "DECREMENT") {
    return { count: state.count - 1 };
  }
  return state;
}

// 2. 创建 store
const store = createStore(reducer);

// 3. 订阅 store(数据变化时触发)
store.subscribe(() => {
  console.log("状态更新了:", store.getState());
});

// 4. 触发数据修改(dispatch)
store.dispatch({ type: "INCREMENT" });
store.dispatch({ type: "INCREMENT" });
store.dispatch({ type: "DECREMENT" });

// 5. 获取最新状态
console.log("最终 count:", store.getState().count);

image.png

二:Redux与React-环境准备

在React中使用redux,官方要求安装两个其他插件

1.Redux Toolkit

2.react-redux

// 1.使用CRA快速创建React项目
npx create-react-app react_redux
// 2.安装配套工具
npm i @reduxjs/toolkit react-redux
// 3.启动项目
npm run start

3.store目录结构

image.png
✅ store/ 目录用来集中管理Redux的状态。
✅ modules/ 目录拆分不同的业务模块(user.js、cart.js)。
✅ index.js 负责整合modules/ 里的reducer并创建store。
✅ React组件可以用useSelector获取store里的数据,用useDispatch修改数据。

三:Redux与React-实现计数器counter

1.Redux store配置

①配置counterStore模块
counterStore.js负责定义计数器的状态(state),并提供修改state的方法(reducer)
createSlice():定义state状态,可以在initialState中定义该模块的初始状态。
创建reducer函数,可以在reducers中定义如何修改state的方法,这些方法会自动变成Redux的reducer。
会自动生成action对象,你可以直接使用它们来触发state的变化
createSlice()把原本需要手写action、reducer和action创建函数的步骤合并成了一个更简洁的方法。

import { createSlice } from "@reduxjs/toolkit";

const counterStore = createSlice({
  name: "counter",  // 这个 store 的名字
  initialState: { count: 0 },  // 初始状态,count 设为 0
  reducers: {
    increment(state) { state.count++ },  // 增加 count
    decrement(state) { state.count-- },  // 减少 count
  }
});

// 导出 action 创建函数
// 提取increment和decrement,作为action创建函数
const { increment, decrement } = counterStore.actions;

// 获取 reducer  
// 提取counterReducer,作为Redux store需要的reducer
const counterReducer = counterStore.reducer;

// 导出increment和decrement,供React组件调用。
// 导出counterReducer,让store认识这个reducer。
export { increment, decrement };
export default counterReducer;

②配置根store并组合counterStore模块
配置store/index.js
configureStore创建store方法,比createStore()更简单
通过configureStore创建全局store
把counterStore里的reducer组合进store

import {configureStore} from "@reduxjs/toolkit"
import counterReducer from "./modules/counterStore"
// 创建根store组合子模块
// configureStore() 是Redux Toolkit提供的简化版 createStore()
const store = configureStore({
  reducer:{
    counter:counterReducer  // 把counterStore加入store
  }
})
export default store

2.React组件

①在React里注入store
Provider用于将Redux store提供给React应用的所有组件,让它们可以访问store里的状态
App组件(和所有子组件都可以通过useSelector获取store里的数据,并通过useDispatch修改数据。

import store from "./store";
import { Provider } from "react-redux";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <Provider store={store}>    // 让React组件都能访问Redux里的state
    <App />
  </Provider>
);

②在React组件里使用store里的数据
用useSelector获取store里的count并在组件中显示。

import { useSelector } from "react-redux";

// useSelector(state => state.counter)
// state.counter → 获取counterStore里的state
// { count } → 取出count并赋值给变量
const { count } = useSelector(state => state.counter);

③在React组件里修改store里的数据
用useDispatch触发increment和decrement方法,从而修改store里的count

import { useDispatch, useSelector } from "react-redux";
// 导入 action 创建函数
import { increment, decrement } from "./store/modules/counterStore";

function App() {
  const { count } = useSelector(state => state.counter);  // 获取 count
  const dispatch = useDispatch();  // 获取 dispatch 用来提交action,触发reducer里的方法

  return (
    <div className="App">
      <button onClick={() => dispatch(decrement())}>-</button>  
      <span>{count}</span>  
      <button onClick={() => dispatch(increment())}>+</button>  
    </div>
  );
}

⭐React组件使用Redux
useSelector获取state
useDispatch触发action修改state
Provider让store可在所有组件使用

四:Redux与React-提交action传参

组件中有两个按钮add to 10 和 add to 20可以直接把count值修改到对应的数字,目标count
值是在组件中传递过去的,需要在提交action的时候传递参数
在reducers的同步修改方法中添加action对象参数,在调用actionCreater的时候传递参数,参数会被传递到action对象payload属性上
store/modules/counterStore.js
定义Redux的状态和修改方法

import {createSlice} from "@reduxjs/toolkit"
const counterStore = createSlice({
  name:"counter",   // 这个模块的名字
  initialState:{
     count:0  // count初始值为0
  },
  // 修改数据的同步方法
  reducers:{
    increment(state){
      state.count++
    },
    decrement(state){
      state.count--
    },
    // 直接把count设为某个值(使用action传参)
    addToNum(state,action){
      state.count = action.payload // action.payload里存着传过来的参数
    }
  }
})

// 解构出创建action对象的函数  {actionCreater}
const {increment,decrement,addToNum} = counterStore.actions
// 获取reducer函数
const counterReducer = counterStore.reducer
// 导出创建action对象的函数和reducer函数
export{increment,decrement}
export default counterReducer

store/index.js
创建 Redux Store
configureStore()创建Redux Store
counterReducer作为counter模块的reducer

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./modules/counterStore"; // 引入counter模块

// 创建Redux Store,管理counter模块
const store = configureStore({
  reducer: {
    counter: counterReducer, // 这里的key "counter" 影响useSelector的使用
  },
});

export default store;

App.js
React组件,使用Redux管理状态

import {useDispatch,useSelector} from "react-redux"
// 导入创建action对象的方法actionCreater
import {increment,decrement,addToNum } from "./store/modules/counterStore"
function App(){
 // 获取Redux里的count状态
 const {count} = useSelector(state => state.counter)
 // 得到dispatch函数
 const dispatch = useDispatch()
  return(
   <div className="App">
   // 调用dispatch提交action对象
   <button onClick={() => dispatch(decrement())}>-</button>
   <span>{count}</span>
   <button onClick={() => dispatch(increment())}>+</button>
   // 传递参数,直接修改count
   <button onClick={() => dispatch(addToNum(10))}>add to 10</button>
   <button onClick={() => dispatch(addToNum(20))}>add to 20</button>
   </div>
  )
}

点击 "add to 10" 按钮时,发生的流程:
①dispatch(addToNum(10))派发(dispatch)一个 action

{ type: "counter/addToNum", payload: 10 }

②这个action被counterReducer处理

addToNum(state, action) {
   state.count = action.payload; // action.payload 里存的是 10
}

③count变成10,Redux状态更新
④eact组件 App.js自动重新渲染,显示最新的count
action.payload:

{
  type: "counter/addToNum",
  payload: 10  // 这个 payload 就是传递过来的参数
}

参数传递的完整流程
①dispatch(addToNum(10)) 触发addToNum
②createSlice()生成的addToNum处理action
③action.payload里保存着10
④state.count = action.payload,所以count变成10
image.png

五:Redux与React-异步状态操作

image.png
异步操作样板代码

1.创建store的写法保持不变,配置好同步修改状态的方法

2.单独封装一个函数,在函数内部return一个新函数,在新函数中

①封装异步请求获取数据
②调用同步actionCreater传入异步数据生成一个action对象,并使用dispatch提交

3.组件中dispatch的写法保持不变

⭐创建Redux模块(Slice)
createSlice用来创建一个Redux模块
reducers里定义了setChannels(state, action),用于同步更新Redux状态,即把 action.payload 里的频道数据存入 channelList。

const channelStore = createSlice({
  name:"channel",
  initialState:{
    channelList:[]
  },
  reducers:{
    setChannels(state,action){
      state.channelList = action.payload
    }
  }
})

⭐创建异步请求的Action
封装一个异步Action(fetchChannelList),用来获取频道数据并存入Redux。
fetchChannelList是一个返回函数的函数,它的内部:
异步请求 axios.get(url) 获取数据
调用setChannels并dispatch(setChannels(数据)),将获取的数据提交到Redux,更新channelList。

const {setChannels} = channelStore.actions
const url = "http://geek.itheima.net/v1_0/channels"
const fetchChannelList = ()=>{
  return async(dispatch)=>{
    const res = await axios.get(url)
    dispatch(setChannels(res.data.data.channels))
  }
}
export {fetchChannelList}

⭐在组件中触发异步请求
在组件挂载时(useEffect),自动发送异步请求,获取频道列表并更新Redux状态。
useDispatch():获取Redux的dispatch方法,组件通过它触发Redux的Action。
useEffect(() => {...}, [dispatch]):组件渲染后自动执行fetchChannelList()
fetchChannelList()会执行异步请求,然后dispatch(setChannels(数据)),最终 Redux状态会更新。

const dispatch = useDispatch()
useEffect(() =>{
   dispatch(fetchChannelList())
   },[dispatch]
})

代码的整体流程
①Redux createSlice定义状态和同步更新方法
②fetchChannelList发送异步请求,并dispatch更新Redux
③组件useEffect触发 fetchChannelList,使Redux自动存储异步数据
④最终组件可以从Redux读取 channelList并显示频道信息
fetchChannelList 就是一个封装好的异步操作
👉 组件dispatch(fetchChannelList()) 触发请求并更新Redux
👉 Redux状态更新后,组件就能拿到最新的数据

ReactRouter

一:ReactRouter

1.概念

一个路径path对应一个组件component,在浏览器中访问一个path的时候,path对应的组件会在页面中进行渲染

const routes = [
  {
   path:'/about',
   component:About,
  },
  {
   path:'/article',
   component:Article,
  },
]

2.基础环境配置

// 1.创建项目并安装所有依赖
npx create-react-app react-router-pro
npm i
// 2.安装最新的ReactRouter包
npm i react-router-dom
// 3.启动项目
npm run start

3.小案例

创建一个可以切换登录页和文章页的路由系统
/login 登录
/article 文章
在index.js文件中
createBrowserRouter:用于创建浏览器路由对象,基于URL进行页面切换。
RouterProvider:用于提供路由,让应用知道如何根据URL显示不同的页面。

import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
// 1.创建router实例对象并且配置路由对应关系
const router = createBrowserRouter([
 {
  path:"/login",
  element:<div>我是登录页</div>
 },
 {
  path:"/article",
  element:<div>我是文章页</div>
 }
])

//  ReactDom.createRoot:获取HTML里的 <div id="root"></div> 作为React渲染的根节点。
// RouterProvider:绑定router,让应用可以根据URL切换页面。
const root = ReactDom.createRoot(document.getElementById("root"))
root.render(
 <React.StrictMode>
 // 2.路由绑定
 <RouterProvider router = {router}></RouterProvider>
 </React.StrictMode>
)

二:抽象路由模块

router配置
image.png

1.src-page

①Login文件夹
Login-index.js

const Article = ()=>{
  return <div>我是文章页</div>

  export default Article
}

②Article文件夹
Article-index.js

const Login = ()=>{
  return <div>我是登录页</div>

  export default Login
}

2.src-router-index.js

import Login from "../page/Login"
import Article from "../page/Article"
import {createBrowserRouter} from "react-router-dom"

const router = createBrowserRouter([
 {
  path:"/login",
  element:<Login/>
 },
 {
  path:"/article",
  element:<Article/>
 }
])

export default router

3.index.js

// 导入路由router
import router from "./router"
 <RouterProvider router = {router}></RouterProvider>

三:路由导航

路由系统中的多个路由之间需要进行路由跳转,并且在跳转的同时有可能需要传递参数进行通信

1.声明式导航

在模块中通过'Link'组件描述要跳转到哪里去
比如后台管理系统的左侧菜单

<Link to = "/article">文章</Link>

2.编程式导航

通过'useNavigate'钩子得到导航方法,然后通过方法以命令式的形式进行路由跳转
通过调用navigate方法传入地址path实现跳转
登录请求完毕之后跳转就可以选择这种方式

import {useNavigate} from "react-router-dom"
const Login = ()=>{
  const navigate = useNavigate()
  return(
     <div>
       我是登录页
       <button onClick={() => navigate("/article")}>跳转到文章页</button>
      </div>
  ) 
}
export default Login

四:导航跳转传参

1.searchParams传参

<button onClick={() => navigate("/article?id=1001&name=jack")}>searchParams传参</button>

Article-index.js

import {useSearchParams} from "react-router-dom"
const Article =()=>{
 const [params] = useSearchParams()
 const id = params.get("id")
 const id = params.get("name")
 return <div>我是文章项{id}-{name}</div>
}

2.params传参

<button onClick={() => navigate("/article/1001/jack")}>params传参</button>

index.js

import { createBrowserRouter} from "react-router-dom";

const router = createBrowserRouter([
 {
  path:'/article/:id/:name',
  element:<Article/>
 }
])

Article-index

import {useSearchParams} from "react-router-dom"
const Article =()=>{
 const params = useParams()
 const id = params.id
 const name = params.name
 return <div>我是文章项{id}-{name}</div>
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?