React筆記

1. 創(chuàng)建react項(xiàng)目

// 1. 全局安裝create-react-app
npm i create-react-app -g
// 2. 查看create-react-app是否安裝成功
create-react-app -v
// 3. 創(chuàng)建react項(xiàng)目, 項(xiàng)目名自定義即可,下面以react-demo為例
npx create-react-app react-demo
// 4. 進(jìn)入新創(chuàng)建的文件目錄下
cd react-demo
// 5. 運(yùn)行項(xiàng)目
npm strat

使用vite創(chuàng)建React項(xiàng)目(推薦)

// 1. 全局安裝vite
npm i vite -g
// 2. 用vite創(chuàng)建React項(xiàng)目
npm create vite
// 3. 選項(xiàng)如下圖,第三步根據(jù)需要選擇js或ts
// 4. 進(jìn)入新創(chuàng)建的文件目錄下
cd react-demo
// 5. 運(yùn)行項(xiàng)目
npm run dev
用vite創(chuàng)建React項(xiàng)目的選項(xiàng)

2. React基本語(yǔ)法

React使用JSX語(yǔ)法,JSX是一種JavaScript的語(yǔ)法擴(kuò)展
語(yǔ)法:在大括號(hào){}中直接插入可識(shí)別的內(nèi)容

  1. 直接在大括號(hào)中使用字符串
  2. 直接在大括號(hào)中使用js變量
  3. 直接在大括號(hào)中插入函數(shù)方法
  4. 直接在大括號(hào)中js對(duì)象
const hello = "我是變量";

function print() {
  return "你好,react";
}

function App() {
  return (
    <div>
      {/* 1. 直接在{}中插入字符串 */}
      <div>{"hello react"}</div>
      {/* 2. 直接在{}中插入js變量 */}
      <div>{hello}</div>
      {/* 3. 直接在{}中插入函數(shù)或方法 */}
      <div>{print}</div>
      {/* 內(nèi)置函數(shù) */}
      <div>{new Date().getDate}</div>
      {/* 4. 直接在{}中插入js對(duì)象 */}
      <div style={{ color: "red" }}>{"hello react"}</div>
    </div>
  );
}
export default App

3. React列表渲染

  • map 循環(huán)數(shù)組結(jié)構(gòu),return結(jié)構(gòu)
  • 注意點(diǎn):循環(huán)時(shí)需要再標(biāo)簽上加上一個(gè)獨(dú)一無(wú)二的key屬性,可以是string和number
  • key的作用是:react內(nèi)部使用,提升渲染性能
const list = [
  { id: 1, name: "張三" },
  { id: 2, name: "李四" },
  { id: 3, name: "王五" },
];

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

export default App;

4. React條件渲染

  • 簡(jiǎn)單的條件判斷可以使用三元運(yùn)算符?;
  • 情況較多的條件可以定義一個(gè)函數(shù),使用if-else或者switch-case判斷后返回對(duì)應(yīng)的標(biāo)簽
const flag = true;

const type = 0;
function getType() {
  if (type === 0) {
    return <div>類(lèi)型為0</div>;
  } else if (type === 1) {
    return <div>類(lèi)型為1</div>;
  } else {
    return <div>類(lèi)型為其他</div>;
  }
}

function App() {
  return (
    <div>
      {/* &&語(yǔ)法,flag為true才顯示 */}
      {flag && <div>flag為true時(shí)顯示</div>}
      {/* 三元運(yùn)算符 */}
      {flag ? <div>flag為true時(shí)顯示</div> : <div>flag為false時(shí)顯示</div>}
      {/* 多中情況,使用函數(shù) */}
      {getType()}
    </div>
  );
}

export default App;

5. React事件綁定

function App() {
  // 基礎(chǔ)事件綁定方式
  function click1() {
    console.log("基礎(chǔ)事件綁定方式");
  }

  // 獲取e參數(shù)
  function click2(e) {
    console.log("獲取e參數(shù)", e);
  }

  // 獲取自定義參數(shù)
  function click3(name) {
    console.log("獲取自定義參數(shù)", name);
  }

  // 獲取自定義參數(shù)和e參數(shù)
  function click4(name,e) {
    console.log("獲取自定義參數(shù)和e參數(shù)",name,e);
  }
  return (
    <div>
      <button onClick={click1}>基礎(chǔ)事件綁定方式</button>
      <br />
      <button onClick={click2}>獲取e參數(shù)</button>
      <br />
      <button onClick={() => click3("張三")}>獲取自定義參數(shù)</button>
      <br />
      <button onClick={(e) => click4("李四", e)}>獲取自定義參數(shù)和e參數(shù)</button>
      <br />
    </div>
  );
}

export default App;

6. React組件

React組件就是以首字母大寫(xiě)的函數(shù),內(nèi)部存放了組件的邏輯和視圖UI,渲染組件只需要把組件當(dāng)成標(biāo)簽書(shū)寫(xiě)

// 定義組件
function Son() {
  return <div>我是子組件</div>;
}

function App() {
  return (
    <div>
      我是父組件
      {/* 自閉和寫(xiě)法 */}
      <Son />
      {/* 成對(duì)標(biāo)簽寫(xiě)法 */}
      <Son></Son>
    </div>
  );
}

export default App;

7. useState狀態(tài)變量

  • useState是一個(gè)React Hook函數(shù),允許向組件添加一個(gè)狀態(tài)變量,從而控制影響組件的渲染結(jié)果,即數(shù)據(jù)驅(qū)動(dòng)視圖,類(lèi)似Vue3中的ref和reactive
  • 修改useState定義的值,只能使用setNum傳入一個(gè)全新的值,改變舊值
import { useState } from "react";

function App() {
  const [num, setNum] = useState(0);
  const [data, setData] = useState({
    name: "Tom",
  });

  return (
    <div>
      {num}
      <button onClick={() => setNum(num + 1)}>加法</button>
      <hr />

      <div>{data.name}</div>
      <button
        onClick={() =>
          setData({
            ...data,
            name: "jock",
          })
        } 
      >
        改變姓名
      </button>
    </div>
  );
}

export default App;

8. React樣式

  • 比較推薦給標(biāo)簽綁定className的樣式寫(xiě)法
import './app.css'

const divStyle = {
  color: 'blue',
  fontSize:'25px'
}

function App() {
  return (
    <div>
      {/* 行內(nèi)樣式,必須在{}中包裹一個(gè)對(duì)象。不推薦這種寫(xiě)法 */}
      <div style={{ color: "red", fontSize: "30px" }}>這是一個(gè)div</div>
      {/* 定義一個(gè)樣式對(duì)象,在style中傳入一個(gè)對(duì)象 */}
      <div style={divStyle}>這是一個(gè)div</div>
      {/* 通過(guò)class類(lèi)名添加樣式,這種寫(xiě)法需要把樣式單獨(dú)寫(xiě)在一個(gè)css文件中,推薦寫(xiě)法 */}
      <div className="div-txt">這是一個(gè)div</div>
    </div>
  );
}

export default App;

9. 表單數(shù)據(jù)雙向綁定

  • React中沒(méi)有像Vue中的v-model指令實(shí)現(xiàn)數(shù)據(jù)的雙向綁定
  • 可以用原生的方式實(shí)現(xiàn)
// 導(dǎo)入聲明狀態(tài)的方法
import { useState } from "react";

function App() {
  // 定義一個(gè)要給表單綁定的值和修改表單的方法
  const [text, setText] = useState("");
  return (
    <div>
      {/* 1. 通過(guò)value屬性綁定input的狀態(tài) */}
      {/* 2. 綁定onChange事件,通過(guò)事件參數(shù)e獲取輸入框中最新的值 */}
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
      />
    </div>
  );
}

export default App;

10. React中獲取DOM

  • 與vue3中的ref寫(xiě)法類(lèi)似
// 導(dǎo)入聲明獲取dom的方法
import { useRef } from "react";

function App() {
  // 定義一個(gè)用useRef生成的對(duì)象,綁定到dom元素標(biāo)簽上
  const buttonRef = useRef(null);
  return (
    <div>元素
      {/* 在dom可用時(shí),通過(guò)生成的useRef對(duì)象的current來(lái)返回dom */}
      <button ref={buttonRef} onClick={() => console.log(buttonRef.current)}>
        獲取按鈕的dom元素
      </button>
    </div>
  );
}

export default App;

11. React組件通信

11.0. props類(lèi)型或默認(rèn)值設(shè)置

通過(guò) 組件名.propTypes來(lái)判斷類(lèi)型,通過(guò)安裝npm i prop-types第三方庫(kù)proptypes實(shí)現(xiàn)類(lèi)型或非空校驗(yàn)
通過(guò) 組件名.defaultProps來(lái)設(shè)置默認(rèn)值,注意:該方式目前主要用在類(lèi)組件中,函數(shù)組件中會(huì)提示報(bào)錯(cuò),函數(shù)組件中使用參數(shù)設(shè)置默認(rèn)值的方法實(shí)現(xiàn)

import React from 'react'
// 函數(shù)組件設(shè)置默認(rèn)值,通過(guò)參數(shù)設(shè)置
function Son({ val,txt='默認(rèn)值' }) {
    return (
        <div>我是子組件
            <p>父組件傳遞的值:{val}</p>
            <p>默認(rèn)值:{txt}</p>
        </div>
    )
}
// Son.propTypes,限制props傳值的類(lèi)型
Son.propTypes = {
    val: (props) => {
        if (typeof props.val !== 'string') {
            throw new Error('val 必須是一個(gè)字符串')
        }
    }
}

// Son.defaultProps,用在類(lèi)組件中,可設(shè)置props的默認(rèn)值,函數(shù)組件中控制臺(tái)會(huì)報(bào)錯(cuò)
// Son.defaultProps = {
//  val: '我是默認(rèn)值',
//  txt: '父組件沒(méi)傳,我就顯示'
// }

export default function App() {
    const val = '你好'
    return (
        <>
            <div>我是父組件</div>
            <hr />
            <Son val={val} />
        </>
    )
}

第三方庫(kù)proptypes的使用

import React from 'react'
// 導(dǎo)入校驗(yàn)props類(lèi)型的庫(kù)
import propTypes from 'prop-types'

// 函數(shù)組件設(shè)置默認(rèn)值,通過(guò)參數(shù)設(shè)置
function Son(props) {
    return (
        <div>我是子組件
            <p>父組件傳遞的值:{props.val}</p>
        </div>
    )
}
// Son.propTypes,限制props傳值的類(lèi)型
Son.propTypes = {
    // 判斷是否是字符串
    /**
     * propTypes可判斷的類(lèi)型:array、bool、func、number、object、string、symbol
     * propTypes.any.isRequired  表示傳一個(gè)不為空的任意類(lèi)型,any可替換為上面支持的類(lèi)型
     * propTypes.oneOfType([propTypes.number,propTypes.string])  表示只能傳數(shù)字和字符串類(lèi)型的數(shù)據(jù)
     * propTypes.oneOf([1,10])  表示只能傳數(shù)字1或10
     * propTypes.arrayOf(propTypes.number)或propTypes.objectOf(propTypes.number)  表示必須傳一個(gè)都是數(shù)字類(lèi)型的數(shù)組或?qū)傩詾閿?shù)字的對(duì)象
     * propTypes.shape({})  用于檢測(cè)對(duì)象每一個(gè)屬性的類(lèi)型
     * (props, propName, componentName) => {// 復(fù)雜的類(lèi)型判斷} 在屬性prop的類(lèi)型檢測(cè)中,屬性值是一個(gè)函數(shù),
     * 在這里props是包含prop的props對(duì)象,propName是prop的屬性名,componentName是props所在的組件名稱(chēng),函數(shù)的返回值是一個(gè)Error對(duì)象
     */
    val: propTypes.string.isRequired
    // val: propTypes.oneOfType([propTypes.number,propTypes.string])
    // val: propTypes.oneOf([1,10])
    // val: propTypes.arrayOf(propTypes.number)
    // val: propTypes.shape({
    //  name:propTypes.string,
    //  age:propTypes.number
    // })
    // val: (props, propName, componentName) => {
    //  // 復(fù)雜的類(lèi)型判斷
    // }
}


export default function App() {
    const val = 11
    return (
        <>
            <div>我是父組件</div>
            <hr />
            <Son val={val} />
        </>
    )
}

11.1. 父子組件通信

  • 父組件傳遞數(shù)據(jù),子組件標(biāo)簽上綁定屬性
  • 子組件接收數(shù)據(jù),props的參數(shù)中獲取到父組件傳遞的數(shù)據(jù)
  • props可以傳遞任意類(lèi)型數(shù)據(jù),包括數(shù)字、字符串、布爾值、數(shù)組、對(duì)象、函數(shù)、JSX
  • props是只讀對(duì)象,不能直接修改傳遞的值,只能由父組件進(jìn)行修改
  • 定義在子組件標(biāo)簽內(nèi)部的內(nèi)容,通過(guò)props.children可以獲取到
// 父子組件通信

// 子組件
function Son(props) {
  return (
    <div>
      {props.text}
      <span style={{ color: "red" }}>{props.children}</span>
    </div>
  );
}

// 父組件
function App() {
  const text = "我是父組件傳遞的值";
  return (
    <div>
      <div>我是父組件</div>
      <Son text={text}>
        <span>子組件內(nèi)部的內(nèi)容</span>
      </Son>
    </div>
  );
}

export default App;

11.2. 子組件給父組件傳值

  • 子傳父可以通過(guò)父組件給子組件傳遞一個(gè)方法,子組件觸發(fā)父組件傳遞的方法,并把想要傳遞的數(shù)據(jù)以參數(shù)的形式綁定在觸發(fā)的父組件方法上,父組件接收即可
// 子組件給父組件傳值

// 子組件
function Son(props) {
  const text = "我是子組件傳遞的值";
  return (
    <div>
      {/* 3. 子組件觸發(fā)父組件定義的方法,把想要傳遞的數(shù)據(jù),以參數(shù)的形式,傳遞給父組件 */}
      <button onClick={() => props.getValue(text)}>給父組件傳值</button>
    </div>
  );
}

// 父組件
function App() {
  const [text, setText] = useState("");
  // 1. 父組件定義一個(gè)方法,把該方法綁定到子組件上
  function getSonMsg(value) {
    // 4. 子組件觸發(fā)該方法時(shí),接收傳遞過(guò)來(lái)的參數(shù),并保存下來(lái)
    setText(value);
  }
  return (
    <div>
      <div>我是父組件</div>
      <div>{text}</div>
      {/* 2. 給子組件綁定父組件的方法 */}
      <Son getValue={getSonMsg} />
    </div>
  );
}

export default App;

11.3. 兄弟組件通信

  • 可以把父組件作為一個(gè)中間橋接,先通過(guò)11.2中的方法,把子組件A的值傳給父組件,父組件接收后,在傳給子組件B
// 兄弟組件傳值

// 子組件A
function A(props) {
  const text = "傳遞給兄弟組件B的數(shù)據(jù)";
  return (
    <div>
      <div>我是子組件A</div>
      {/* 3. 子組件A觸發(fā)父組件定義的方法,把想要傳遞的數(shù)據(jù),以參數(shù)的形式,傳遞給父組件 */}
      <button onClick={() => props.getValue(text)}>給兄弟組件傳值</button>
    </div>
  );
}

// 子組件B
function B(props) {
  return (
    <div>
      <div>我是子組件B</div>
      {/* 6. 接收來(lái)自子組件A的數(shù)據(jù) -> 經(jīng)過(guò)父組件 -> 最后到達(dá)子組件B */}
      <div> {props.text}</div>
    </div>
  );
}

// 父組件
function App() {
  const [text, setText] = useState("");
  // 1. 父組件定義一個(gè)方法,把該方法綁定到子組件A上
  function getSonMsg(value) {
    // 4. 子組件A觸發(fā)該方法時(shí),接收傳遞過(guò)來(lái)的參數(shù),并保存下來(lái)
    setText(value);
  }
  return (
    <div>
      <div>我是父組件</div>
      <div>{text}</div>
      {/* 2. 給子組件A綁定父組件的方法 */}
      <A getValue={getSonMsg} />
      {/* 5. 父組件把接收到子組件A中的數(shù)據(jù)傳遞給子組件B */}
      <B text={text} />
    </div>
  );
}

export default App;

11.4. 爺組件傳孫組件或父組件傳子組件

  • 這個(gè)的方法類(lèi)似于Vue3中的依賴(lài)注入,provide和inject
// 爺組件傳孫組件或父組件傳子組件

// 導(dǎo)入鉤子函數(shù)
import { createContext, useContext } from "react";

// 1. 創(chuàng)建一個(gè)Context,一般首字母大寫(xiě)
const TextCtx = createContext();

// 子組件A
function A() {
  return (
    <div>
      <div>我是子組件A</div>
      <B />
    </div>
  );
}

// 孫組件B
function B() {
  // 3. 通過(guò)鉤子函數(shù)useContext獲取爺組件通過(guò)TextCtx.Provider傳遞過(guò)來(lái)的數(shù)據(jù)
  const text = useContext(TextCtx);
  return (
    <div>
      <div>我是孫組件B</div>
      {/* 4. 渲染爺組件傳遞過(guò)來(lái)的數(shù)據(jù) */}
      <div>{text}</div>
    </div>
  );
}

// 爺組件
function App() {
  const text = "我是爺爺組件的數(shù)據(jù)";
  return (
    <div>
      {/* 2. 把需要接收數(shù)據(jù)的組件用TextCtx.Provider標(biāo)簽包裹起來(lái),并綁定value屬性 */}
      <TextCtx.Provider value={text}>
        <div>我是爺組件</div>
        <A />
      </TextCtx.Provider>
    </div>
  );
}

export default App;

12. useEffect副作用函數(shù)

使用useEffect,可以直接在函數(shù)組件內(nèi)處理生命周期事件。 根據(jù)React class 的生命周期函數(shù),可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個(gè)函數(shù)的組合。

import {useEffect,useState} from 'react'

function App() {
    // useEffect副作用函數(shù)執(zhí)行時(shí)機(jī),也就是該函數(shù)的第二個(gè)參數(shù)不同的配置
    // 1. 沒(méi)有依賴(lài),也就是不傳第二個(gè)參數(shù),執(zhí)行時(shí)機(jī)是:初始+數(shù)據(jù)更新
    useEffect(()=>{
        conosle.log('初始+數(shù)據(jù)更新')
    })
    
    // 2. 第二個(gè)參數(shù)傳空數(shù)組,執(zhí)行時(shí)機(jī)是:初始
    useEffect(()=>{
        // 類(lèi)似于vue中的mounded函數(shù),可以發(fā)起請(qǐng)求和操作dom
        conosle.log('初始')
    },[])
    
    // 3. 第二個(gè)參數(shù)傳特定依賴(lài)項(xiàng),執(zhí)行時(shí)機(jī)是:初始+依賴(lài)項(xiàng)變化時(shí)
    const [count,setCount] = useState(0)
    useEffect(()=>{
        // 類(lèi)似于vue中的watch監(jiān)聽(tīng)
        conosle.log('初始+依賴(lài)項(xiàng)變化時(shí)')
    },[count])
    
  return (
    <div>
            <div>{count}</div>
            <button onClick={()=>setCount(count+1)}>加法</button>
    </div>
  );
}
export default App

清除副作用函數(shù)

import {useEffect, useState} from 'react';

function Son() {
    useEffect(() => {
        const timer = setInterval(() => {
            console.log('定時(shí)器執(zhí)行中')
        })
        // 清除副作用函數(shù),在卸載時(shí)執(zhí)行
        return () => clearInterval(timer)
    }, []);

    return <div>我是子組件</div>;
}

function App() {
    const [show, setShow] = useState(true)
    return (
        <div>你好 react
            {show && <Son/>}
            <div>
                <button onClick={() => setShow(!show)}>隱藏子組件</button>
            </div>
        </div>
    )
}

export default App

13. useMemo和useCallback

  • useMemo是計(jì)算屬性,用于緩存屬性,類(lèi)似于Vue中的computed
  • useCallback用于緩存方法
import React, { useCallback, useMemo, useState } from 'react'

export default function Home() {
    const [num, setNum] = useState(0)
    const text = '  你好'
    const add = () => {
        setNum(num + 1)
    }

    // 計(jì)算屬性u(píng)seMemo
    // 該hook的第二個(gè)參數(shù)是一個(gè)數(shù)組
    // 第二個(gè)參數(shù),其中包含了需要監(jiān)視變化的變量
    // 當(dāng)數(shù)組中的屬性發(fā)生變化時(shí),useMemo 會(huì)重新執(zhí)行計(jì)算函數(shù),返回新的計(jì)算結(jié)果。
    // 如果數(shù)組中的屬性沒(méi)有發(fā)生變化,則直接使用緩存的計(jì)算結(jié)果。
    const newNum = useMemo(() => {
        console.log('useMemo');
        return num + text
    },[num])

    // useCallback, useMemo,useEffect第二個(gè)參數(shù)的用法都一樣
    const add1 = useCallback(()=>{
        setNum(num + 1)
    })
    return (
        <div>
            <h2>{num}</h2>
            <button onClick={add}>加1</button>
            <button onClick={add1}>加1</button>
            <hr />
            <h2>{newNum}</h2>
        </div>
    )
}

14. 封裝自定義Hook

封裝Hook,目的是為了復(fù)用

  1. 定義一個(gè)use開(kāi)頭的函數(shù)
  2. 把邏輯定義到use的函數(shù)中,并以對(duì)象或數(shù)組的形式向外界return出去,供外界使用的屬性或方法
  3. 在需要使用的地方,解構(gòu)出自定義Hook中return出來(lái)的屬性或方法,才可以使用
import {useState} from 'react';

// 封裝自定義Hook,作用是可以多次復(fù)用
function useToggle() {
    const [show, setShow] = useState(true)
    const onToggle = () => setShow(!show)

    return {
        show,
        onToggle
    }
}

function App() {
    const {show, onToggle} = useToggle();
    return (
        <div>你好 react
            {show && <div>顯示與隱藏</div>}
            <div>
                <button onClick={onToggle}>隱藏子組件</button>
            </div>
        </div>
    )
}

export default App

15. Redux狀態(tài)管理

Redux 是一個(gè)小型的獨(dú)立 JS 庫(kù)。 但是,它通常與其他幾個(gè)包一起使用:

React-Redux

Redux 可以集成到任何的 UI 框架中,其中最常見(jiàn)的是 React 。React-Redux 是我們的官方包,它可以讓 React 組件訪問(wèn) state 片段和 dispatch actions 更新 store,從而同 Redux 集成起來(lái)。

Redux Toolkit

Redux Toolkit 是我們推薦的編寫(xiě) Redux 邏輯的方法。 它包含我們認(rèn)為對(duì)于構(gòu)建 Redux 應(yīng)用程序必不可少的包和函數(shù)。 Redux Toolkit 構(gòu)建是我們建議的最佳實(shí)踐中,簡(jiǎn)化了大多數(shù) Redux 任務(wù),預(yù)防了常見(jiàn)錯(cuò)誤,并使編寫(xiě) Redux 應(yīng)用程序變得更加容易。

Redux DevTools 擴(kuò)展

Redux DevTools 擴(kuò)展 可以顯示 Redux 存儲(chǔ)中狀態(tài)隨時(shí)間變化的歷史記錄。這允許你有效地調(diào)試應(yīng)用程序,包括使用強(qiáng)大的技術(shù),如“時(shí)間旅行調(diào)試”。

  • Redux是React最常用的集中狀態(tài)管理工具,類(lèi)似于Vue中的Pinia(Vuex),可以獨(dú)立于框架外運(yùn)行
  • @reduxjs/toolkit是Redux官方推薦的工具庫(kù),是對(duì) Redux 的二次封裝,它提供了一些便捷的API和工具,幫助開(kāi)發(fā)者更快速地編寫(xiě)Redux代碼
  • react-redux是Redux官方提供的React綁定庫(kù),它提供了一些React Hooks和組件,用于在React組件中訪問(wèn)Redux store和派發(fā)Action

15.1 同步代碼

  1. 安裝工具庫(kù)
// 安裝工具庫(kù)
npm i @reduxjs/toolkit react-redux
  1. 在src文件夾創(chuàng)建store文件夾,在企業(yè)開(kāi)發(fā)中,不同的功能會(huì)單獨(dú)創(chuàng)建一個(gè)文件,統(tǒng)一放在modules目錄中,最后整合到index文件中,統(tǒng)一導(dǎo)出即可。目錄如下


    image.png
  2. 創(chuàng)建countStore.js文件

import { createSlice } from '@reduxjs/toolkit'

const countsStore = createSlice({
    // 名稱(chēng)
    name: 'counts',
    // 初始化state,用于存儲(chǔ)數(shù)據(jù)狀態(tài)
    initialState: {
        count: 0
    },
    // 修改狀態(tài)的方法,支持直接修改,同步方法
    reducers: {
        increment(state) {
            state.count++
        },
        decrement(state) {
            state.count--
        },
        // 通過(guò)actions.payload獲取傳遞來(lái)的參數(shù)
        addToNumber(state, actions) {
            state.count += actions.payload
        }
    }
})

// 從倉(cāng)庫(kù)countsStore的actions中解構(gòu)出對(duì)應(yīng)的方法
const { increment, decrement, addToNumber } = countsStore.actions

// 從倉(cāng)庫(kù)countsStore的reducer中獲取reducers
const reducers = countsStore.reducer

// 按需導(dǎo)出方法
export { increment, decrement, addToNumber }

// 默認(rèn)導(dǎo)出reducers
export default reducers
  1. index.js文件
import {configureStore} from '@reduxjs/toolkit'

// 導(dǎo)入子模塊的reducer
import countsReducer from './modules/countStore'

const store = configureStore({
    reducer:{
        counts:countsReducer
    }
})

// 導(dǎo)出store,供所有組件使用
export default store
  1. 為React注入store
    react-redux負(fù)責(zé)把Redux和React連接起來(lái),內(nèi)置Provider組件通過(guò)store參數(shù)把創(chuàng)建好的store實(shí)例導(dǎo)入到入口文件中

入口文件index.js或main.jsx文件

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

// 導(dǎo)入store
import store from './store'
import {Provider} from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>,
)
  1. 在React組件中使用store中的數(shù)據(jù)和方法
  • 使用react-redux中的useSelector方法,可以使用store中的數(shù)據(jù)
  • 使用react-redux中的useDispatch方法,可以調(diào)用store中的方法
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement,addToNumber } from "./store/modules/countStore.js";

function App() {
    const { count } = useSelector(state => state.counts);
    const dispatch = useDispatch();
    return (
        <div>
            <button style={{ marginRight: '10px' }} onClick={() => dispatch(decrement())}>減法</button>
            <span>{count}</span>
            <button style={{ marginLeft: '10px' }} onClick={() => dispatch(increment())}>加法</button>
            <button style={{ marginLeft: '20px' }} onClick={() => dispatch(addToNumber(20))}>每次加20</button>
        </div>
    )
}

export default App

15.2 異步代碼

  1. 安裝工具庫(kù)
// 安裝工具庫(kù)
npm i axios
  1. 在src文件夾創(chuàng)建store文件夾,在企業(yè)開(kāi)發(fā)中,不同的功能會(huì)單獨(dú)創(chuàng)建一個(gè)文件,統(tǒng)一放在modules目錄中,最后整合到index文件中,統(tǒng)一導(dǎo)出即可。目錄如下


    image.png
  1. 創(chuàng)建txtListStore.js文件
import { createSlice } from "@reduxjs/toolkit";
import axios from 'axios'

const txtListStore = createSlice({
    name: 'txtList',
    initialState: {
        txtList: []
    },
    reducers: {
        // 定義同步方法,修改txtList的值
        getTxtList(state, actions) {
            state.txtList.unshift(actions.payload)
        }
    }
})

const { getTxtList } = txtListStore.actions

// 定義異步方法
const reqGetTxtList = () => {
    return async (dispatch) => {
        const { data: { content } } = await axios.get('https://api.uomg.com/api/rand.qinghua')
        
        // 調(diào)用同步方法,把獲取的異步數(shù)據(jù),傳送過(guò)來(lái),并修改state的狀態(tài)
        dispatch(getTxtList(content))
    }
}
// 從倉(cāng)庫(kù)txtListStore的reducer中獲取reducers
const reducers = txtListStore.reducer

// 導(dǎo)出異步方法
export { reqGetTxtList }

// 默認(rèn)導(dǎo)出reducers
export default reducers
  1. index.js文件
import { configureStore } from '@reduxjs/toolkit'

// 導(dǎo)入子模塊的reducer
import countsReducer from './modules/countStore'
import txtListReducer from './modules/txtListStore'


const store = configureStore({
    reducer: {
        counts: countsReducer,
        txtList: txtListReducer
    }
})

// 導(dǎo)出store,供所有組件使用
export default store
  1. 為React注入store
    react-redux負(fù)責(zé)把Redux和React連接起來(lái),內(nèi)置Provider組件通過(guò)store參數(shù)把創(chuàng)建好的store實(shí)例導(dǎo)入到入口文件中

入口文件index.js或main.jsx文件

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

// 導(dǎo)入store
import store from './store'
import {Provider} from 'react-redux'

ReactDOM.createRoot(document.getElementById('root')).render(
    <React.StrictMode>
        <Provider store={store}>
            <App/>
        </Provider>
    </React.StrictMode>,
)
  1. 在React組件中使用store中的數(shù)據(jù)和方法
  • 使用react-redux中的useSelector方法,可以使用store中的數(shù)據(jù)
  • 使用react-redux中的useDispatch方法,可以調(diào)用store中的方法
  • 可選擇使用useEffect方法,在頁(yè)面初始化時(shí),調(diào)用異步方法,獲取數(shù)據(jù)
import { useSelector, useDispatch } from "react-redux";
import { reqGetTxtList } from "./store/modules/txtListStore.js";
import { useEffect } from "react";

function App() {
    const dispatch = useDispatch();
    const { txtList } = useSelector(state => state.txtList)
    // 使用useEffect,在頁(yè)面初始化時(shí),調(diào)用異步請(qǐng)求,獲取數(shù)據(jù)
    useEffect(() => {
        dispatch(reqGetTxtList())
    }, [reqGetTxtList])

    return (
        <div>
            <h3>土味情話列表</h3>
            <button style={{ marginLeft: '20px' }} onClick={() => dispatch(reqGetTxtList())}>獲取情話</button>
            <ul>
                {txtList.map((item,index)=>(<li key={index}>{item}</li>))}
            </ul>
        </div>
    )
}

export default App

16. React-router路由

16.1 安裝路由插件

npm i react-router-dom

16.2 常用路由標(biāo)簽components

  • <BrowserRouter> 瀏覽器模式,不帶#的路徑
  • <HashRouter> 哈希模式,帶#的路徑
  • <Routes>和<Route>路由出口,需要配合使用,Routes必須包裹著Route,Route中的caseSensitive可以設(shè)置路徑是否區(qū)分大小寫(xiě)
  • <Link>路由鏈接,不高亮
  • <NavLink>路由鏈接,高亮
  • <Navigate>重定向,必須設(shè)置to,有push和replace兩種跳轉(zhuǎn)模式,默認(rèn)push
  • <Outlet>嵌套路由的出口

16.3 基本使用,下面配置只是其中一種方式,并不是最優(yōu)方式

16.3.1 在src目錄下創(chuàng)建routes/index.js文件夾


路由表routes/index.js文件配置如下

import Home from "../pages/home";
import Library from "../pages/library";
import App from "../App";

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

const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { path: '', element: <Navigate to='home' /> },
            { path: 'home', element: <Home /> },
            { path: 'library', element: <Library /> },
        ]
    }
])

export default router

16.3.2 在入口文件index.js或main.js文件中添加路由表配置

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

import { RouterProvider } from 'react-router-dom'
// 導(dǎo)入路由表
import router from './routes'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
    <RouterProvider router={router}>
        <App />
    </RouterProvider>
)

16.3.3 調(diào)整App文件

import React from 'react'
import { Outlet, NavLink } from 'react-router-dom'

const linkStyle = {
    display: 'block',
    marginTop: '20px',
    border: '1px soild black'
}
const navStyle = {
    width: '200px',
    height: '100vh',
    algin: 'center',
    backgroundColor: '#c9c9c9'
}

export default function App() {
    return (
        <div style={{ display: 'flex' }}>
            <div style={navStyle}>
                <NavLink style={linkStyle} to='/home'>Home</NavLink>
                <NavLink style={linkStyle} to='/library'>Library</NavLink>
            </div>
            <Outlet />
        </div >
    )
}

16.4 三種路由傳參及接收參數(shù)

使用const navigate = useNavigate()的navigate方法,實(shí)現(xiàn)編程式路由導(dǎo)航

也可參考更詳細(xì)的配置:摘抄路徑為 https://juejin.cn/post/7203156544878542904

  1. 動(dòng)態(tài)路由傳參
    • 路由表中的路徑格式需要配置為/home/:xx/:xx
    • 在useParams中結(jié)構(gòu)出傳遞的參數(shù)
  2. search傳參
    • 路由路徑不用額外配置
    • 在useSearchParams中結(jié)構(gòu)出兩個(gè)方法來(lái)獲取參數(shù)和修改參數(shù)
  3. state傳參
    • 第二個(gè)參數(shù)是一個(gè)配置對(duì)象,{replace:true|false,state:{}}
    • 在useLocation中結(jié)構(gòu)出state對(duì)象,state對(duì)象就是傳遞的參數(shù)
    • replace用來(lái)是否可以返回上頁(yè)歷史
    • state用來(lái)鍵值對(duì)傳參

App.js文件

import React from 'react'
import { Outlet, useNavigate } from 'react-router-dom'

const navStyle = {
    width: '200px',
    height: '100vh',
    algin: 'center',
    lineHeight: '30px',
    backgroundColor: '#c9c9c9'
}

export default function App() {
    // 定義編程式的hook
    const navigate = useNavigate()

    // 動(dòng)態(tài)路由傳參,路由表的路徑格式為/home/:xx/:xx
    // 在useParams中結(jié)構(gòu)出傳遞的參數(shù)
    const goHome = () => {
        navigate('/home/123/張三')
    }
    // search傳參,路由路徑不用額外配置
    // 在useSearchParams中結(jié)構(gòu)出兩個(gè)方法來(lái)獲取參數(shù)和修改參數(shù)
    const goAbout = () => {
        navigate('/about/?id=999&name=王五')
    }
    // state傳參,第二個(gè)參數(shù)是一個(gè)配置對(duì)象,{replace:true|false,state:{}}
    // replace用來(lái)是否可以返回上頁(yè)歷史
    // state用來(lái)鍵值對(duì)傳參
    const goLibrary = () => {
        navigate('/library', { state: { id: '01', bookName: '三毛流浪記' } })
    }
    return (
        <div style={{ display: 'flex' }}>
            <div style={navStyle}>
                <button onClick={goHome}>跳轉(zhuǎn)到Home,動(dòng)態(tài)傳參</button>
                <button onClick={goAbout}>跳轉(zhuǎn)到About,Search傳參</button>
                <button onClick={goLibrary}>跳轉(zhuǎn)到Library,state傳參</button>
            </div>
            <Outlet />
        </div >
    )
}

routes/index.js路由表配置文件

// 導(dǎo)入路由組件
import Home from "../pages/home";
import Library from "../pages/library";
import About from "../pages/about";
import App from "../App";

// 404路由組件
import NotFound from "../pages/notFound";

// 導(dǎo)入路由相關(guān)方法
import { createBrowserRouter, Navigate } from "react-router-dom";

const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { path: '', element: <Navigate to='home' /> },
            { path: 'home/:id/:name', element: <Home /> },
            { path: 'library', element: <Library /> },
            { path: 'about', element: <About /> },
            { path: '*', element: <NotFound /> }
        ]
    }
])

export default router

home.js路由文件

import React from 'react'
import { useParams } from 'react-router-dom'

export default function Home() {
    // 通過(guò)動(dòng)態(tài)路由傳遞的參數(shù),可以在useParams中結(jié)構(gòu)
    const { id, name } = useParams()
    return (<div>
        <h1>Home主頁(yè)</h1>
        <hr />
        <h4>路由動(dòng)態(tài)傳參,并接收參數(shù)</h4>
        <h6>id: {id}</h6>
        <h6>姓名: {name}</h6>
    </div>)
}

about.js路由文件

import React from 'react'
import { useSearchParams } from 'react-router-dom'

export default function About() {
    // 通過(guò)search路由傳遞的參數(shù),可以在useSearchParams中結(jié)構(gòu)出兩個(gè)方法
    const [searchParams, setSearchParams] = useSearchParams()
    // 調(diào)用searchParams的get方法,獲取對(duì)應(yīng)的參數(shù)
    const id = searchParams.get('id')
    const name = searchParams.get('name')

    return (<div>
        <h1>About關(guān)于</h1>
        <hr />
        <h4>search傳參,并接收參數(shù)</h4>
        <h6>id: {id}</h6>
        <h6>姓名: {name}</h6>
        {/* setSearchParams可以響應(yīng)式的修改search傳遞的參數(shù) */}
        <button onClick={()=>setSearchParams({id:'333',name:'小明'})}>修改search傳遞的參數(shù)</button>
    </div>)
}

library.js路由文件

import React from 'react'
import { useLocation } from 'react-router-dom'

export default function Library() {
    // 通過(guò)state路由傳遞的參數(shù),可以在useLocation中結(jié)構(gòu)出state對(duì)象
    const { state } = useLocation()
    // state對(duì)象就是通過(guò)對(duì)象傳參的內(nèi)容
    const { id, bookName } = state
    return (<div>
        <h1>Library圖書(shū)館</h1>
        <hr />
        <h4>state路由傳遞的參數(shù),并接收參數(shù)</h4>
        <h6>id: {id}</h6>
        <h6>圖書(shū)名稱(chēng): {bookName}</h6>
    </div>)
}

16.5 利用React高階組件,重定向路由

  • 如果沒(méi)有登錄,通過(guò)高階組件,重定向到登錄頁(yè)面

高階組件withLogin.js文件

import React from 'react'
import { Navigate } from 'react-router-dom'

// 企業(yè)項(xiàng)目中,是否登錄標(biāo)識(shí)一般從redux中獲取
const isLogin = false
// Comp接收一個(gè)組件
const WithLogin = (Comp) => {

    // 如已經(jīng)登錄,則放行
    if (isLogin) return Comp

    // 沒(méi)登錄時(shí),重定向到登錄頁(yè)面
    return <Navigate to='/login' replace />
    
}

export default WithLogin

routes/index.js

// 導(dǎo)入路由組件
import Home from "../pages/home";
import Library from "../pages/library";
import About from "../pages/about";
import Login from "../pages/login";
import App from "../App";

// 導(dǎo)入控制登錄鑒權(quán)的高階組件
import WithLogin from "../component/withLogin";

// 404路由組件
import NotFound from "../pages/notFound";

// 導(dǎo)入路由相關(guān)方法
import { createBrowserRouter, Navigate } from "react-router-dom";

const router = createBrowserRouter([
    {
        path: '/',
        element: <App />,
        children: [
            { path: '', element: <Navigate to='home' /> },
            { path: 'login', element: <Login /> },
            { path: 'home/:id/:name', element: <Home /> },
            { path: 'library', element: (WithLogin(<Library />)) },
            { path: 'about', element: <About /> },
            { path: '*', element: <NotFound /> }
        ]
    }
])

export default router

17. React高階組件

  • 高階組件(HOC)是 React 中用于重用組件邏輯的高級(jí)技術(shù)。 HOC 本身不是 React API 的一部分。 它們是從 React 構(gòu)思本質(zhì)中浮現(xiàn)出來(lái)的一種模式。
  • 高階組件是一個(gè)函數(shù),能夠接受一個(gè)組件并返回一個(gè)新的組件
  • React的高階組件類(lèi)似于Vue中的混入mixins
  • 用途:登錄鑒權(quán)操作、增強(qiáng)props、擴(kuò)展組件公共方法...

例:增強(qiáng)props的用法

  1. 創(chuàng)建hoc文件夾,創(chuàng)建withNum.js文件
    withNum.js文件
import React, { useState } from 'react'

// 增強(qiáng)props的高階組件
export default function withNum(Conm) {
    return (props) => {
        // 定義state
        const [num, setNum] = useState(0)
        // 定義加法函數(shù)
        const add = () => {
            setNum(num + 1)
        }
        // {...props} 表示把組件原本的props還傳給組件
        return <Conm num={num} add={add} {...props} />
    }
}
  1. 把需要使用高階組件的普通組件,傳給高階組件
    addNum.js文件
import React from 'react'
import withNum from '../hocs/withNum'

function ClickCounter(props) {
    return (
        <div>
            {/* 父組件傳遞的值 */}
            <h1>{props.text}</h1>
            {/* 通過(guò)props接收到高階組件中定義的屬性和方法 */}
            <p>{props.num}</p>
            <button onClick={props.add}>加法</button>
        </div>
    )
}

export default withNum(ClickCounter)
  1. 把a(bǔ)ddNum組件導(dǎo)入到需要使用的位置
import React from 'react'
import ClickCounter from './addNum'

export default function Home() {
    const text = 'Hello React'
    return (<div>
        <ClickCounter text={text} />
    </div>)
}

18. 類(lèi)組件中的生命周期

  • 類(lèi)組件中分為三個(gè)階段:掛載、更新、卸載
image.png

常用生命周期鉤子

  1. 掛載階段
鉤子函數(shù) 觸發(fā)時(shí)機(jī) 作用
constructor 創(chuàng)建組件時(shí),最先執(zhí)行,初始化的時(shí)候只執(zhí)行一次 1. 初始化state 2. 創(chuàng)建 Ref 3. 使用 bind 解決 this 指向問(wèn)題等
render 每次組件渲染都會(huì)觸發(fā) 渲染UI(注意: 不能在里面調(diào)用setState() )
componentDidMount 組件掛載(完成DOM渲染)后執(zhí)行,初始化的時(shí)候執(zhí)行一次,在瀏覽器更新視圖之前調(diào)用 1. 發(fā)送網(wǎng)絡(luò)請(qǐng)求 2.DOM操作
  1. 更新階段
鉤子函數(shù) 觸發(fā)時(shí)機(jī) 作用
getDerivedStateFromProps(nextProps,preState) 在初始化和更新時(shí)都會(huì)被調(diào)用 用于根據(jù)屬性的變化計(jì)算并返回新的狀態(tài)的生命周期方法,適用于 state 的值在任何時(shí)候都取決于 props 的情況
shouldComponentUpdate(nextProps, nextState) 在組件更新之前被調(diào)用 可以控制組件是否進(jìn)行更新, 返回 true 時(shí)組件更新, 返回 false 則不更新。在這個(gè)階段可以拿到上一個(gè)狀態(tài)Dom元素的坐標(biāo)、大小等相關(guān)信息
componentWillUpdate 在DOM更新之前觸發(fā) 用于處理更新前的一些操作
render 每次組件渲染都會(huì)觸發(fā) 渲染UI(注意: 不能在里面調(diào)用setState() )
componentDidUpdate DOM和狀態(tài)更新后觸發(fā),參數(shù)包括上一個(gè)狀態(tài)的值 可用于比較新舊狀態(tài)并進(jìn)行相應(yīng)的操作
  1. 卸載階段
鉤子函數(shù) 觸發(fā)時(shí)機(jī) 作用
componentWillUnmount 組件卸載(從頁(yè)面中消失) 執(zhí)行清理工作(比如:清理定時(shí)器等)

19. React的性能優(yōu)化

請(qǐng)參考這篇文章:https://blog.csdn.net/m0_65121454/article/details/132366339

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容