React函数式的使用(v18+)


theme: devui-blue

highlight: monokai-sublime

一、React-Router-v6的学习

文档:https://reactrouter.com/

1、下载

bash npm install react-router-dom

2、在App.js中引入界面和React-Router的方法

```javascript import { BrowserRouter, Routes, Route, Link } from 'react-router-dom' import Home from './pages/home/Index.jsx' import Mine from './pages/mine/Mine.jsx' function App() { return (

to="/home">about to="/mine">mine
} /> } /> } />
) }

export default App ```

注:① BrowserRouter 声明一个当前要用非hash模式的路由;

​ ② Link 指定路由跳转组件,to用来配置路由地址;

​ ③ Routes路由出口,路由对应的组件,会在这里 进行渲染;

​ ④ Route 指定路径和组件的对应关系,有几个理由,就写几个,path代表路径,element代表组件;

3、路由的核心组件

1)路由的两种模式(HashRouter、BrowserRouter)

image-20220411234853146

① HashRouter(哈希模式) 带 # 号

② BrowserRouter 不带 # 号(使用了H5的history.pushState API来实现)

2)Link的使用

作用 :用于指定导航链接,完成路由跳转

语法说明: 组件通过to属性指定路由地址,最终会渲染为a链接元素

image-20220419185102653

3)Routes

提供一个路由出口,满足条件的路由组件会渲染到组件内部,定义path和组件的对应关系

image-202204191852013

4)Route

作用 :用于指定导航链接,完成路由匹配(也就是定义url)

语法说明: path属性指定匹配的路径地址,element属性指定要渲染的组件

image-20220419190009913

4、编程式导航

1)在App.js中引入组件并定义url

image-20220419221921461

2)在组件 或 页面中使用

javascript // 1、引入 import { useNavigate } from "react-router-dom" export default function Home(){ // 2、执行 const navigation = useNavigate() const goJump = () => { navigation("/mine") } return <div> 首页 <div> {/* 3、跳转的方式 */} <button onClick={() => navigation("/mine")}>点击跳转</button> <button onClick={goJump}>点击跳转</button> </div> </div> }

image-20220419222348370

3)传参

① searchParams传参

a、传参

javascript // 1、引入 import { useNavigate } from "react-router-dom" export default function Home(){ // 2、执行 const navigation = useNavigate() /** * navigation 调用时 可配置是否要记录历史 * replace 为true 时不记录 */ const goJump = () => { navigation("/mine?id=11111&name=拉斯", { replace: true }) } return <div> 首页 <div> {/* 3、跳转的方式 */} {/* <button onClick={() => navigation("/mine")}>点击跳转</button> */} <button onClick={goJump}>点击跳转</button> </div> </div> }

b、取参

javascript import { useSearchParams } from "react-router-dom" export default function Mine(){ // 获取路由传参 const [ params ] = useSearchParams(); const id = params.get("id") const name = params.get("name") return ( <div> <p>我的</p> <p>id为{ id }</p> <p>name为{ name }</p> </div> ) }

② params传参

a、在路由url中加入参数键

image-20220423211732929

b、在A页面传参

javascript // 1、引入 import { useNavigate } from "react-router-dom" export default function Home(){ // 2、执行 const navigation = useNavigate() /** * navigation 调用时 可配置是否要记录历史 * replace 为true 时不记录 */ const goJump = () => { navigation("/mine/11111", { replace: true }) } return <div> 首页 <div> {/* 3、跳转的方式 */} <button onClick={goJump}>点击跳转</button> </div> </div> }

c、在B页面取参

javascript import { useParams } from "react-router-dom" export default function Mine(){ // 获取路由传参 const params = useParams() return ( <div> <p>我的</p> <p>id为{ params.id }</p> </div> ) }

5、二级路由

1)在App.js中配置二级路由

javascript import { HashRouter, Routes, Route, Link } from 'react-router-dom' import Home from './pages/home/Index.jsx' import HomeOne from './pages/home-one/index.js' import HomeTwo from './pages/home-two/index.js' function App() { return ( <div className="App"> <HashRouter> <div className="menu"> <Link to="/home">about</Link> </div> <Routes> {/* 谁的二级路由,就写在谁的里面 */} <Route path="/home" element={<Home />}> {/* 定义二级路由是 不加斜杠 */} <Route path="home-one" element={<HomeOne />} /> <Route path="home-two" element={<HomeTwo />} /> </Route> <Route path="/" element={<Home />} /> </Routes> </HashRouter> </div> ) } export default App

2)在一级路页面的写法

javascript // 1、引入 import { Outlet } from "react-router-dom" export default function Home(){ return <div> 首页 <div> <hr /> {/* home下有二级路由,所以在这写二级路由的出口 */} <Outlet /> </div> </div> }

3)二级路由中的界面

javascript function HomeOne() { return <div>home的子界面1</div> } export default HomeOne

image-20220423233734397

4)默认选中的二级路由

image-20220423233952333

6、404页面配置

1)pages下新建一个404.js文件

javascript function NoFound() { return ( <div> <h1>404</h1> </div> ) } export default NoFound

2)在路由配置文件中的写法

image-20220423234458883

7、匹配当前选中项,要把Link替换成 NavLink

image-20220424001054792

二、hook的学习

注:hook只能在函数式组件中使用;

1、useState的使用

1)简单使用

① useState的参数式初始值

② useState返回的时一个数组,可以结构出来,但是结构类型时固定的

a、第一个元素是 值

b、第二个元素,是一个函数,是用来修改初始值的(基于原值计算,得到新值)

c、num、setNum是一对的 是绑定在一起的,setNum只能修改num,不能修改别的

javascript import { useState } from "react"; function App() { const [num, setNum] = useState(0) return ( <div> <button onClick={() => setNum(num + 1)}>{num}</button> </div> ) } export default App

2)useState下组件的更新过程

① 首次渲染,组件内部代码会执行,同时useState也会执行,这个时候 会进行初始值的赋值

② 调用第二个元素(setNum),整App都会重新执行

③ 第二次渲染之后,得到的num值,就是1了,模板会重新渲染,因为react会有一个数据状态记录

image-20220427002349021

3)使用规则

① useState 函数可以执行多次,每次执行互相独立,每调用一次为函数组件提供一个状态

② 不能嵌套在if/for/其它函数中(react按照hooks的调用顺序识别每一个hook)

javascript import { useState } from "react"; function App() { console.log("组件更新"); /** * 不能按照斜面这样写(因为这个 和 react内部的运行机制有关) */ if(true) { const [num, setNum] = useState(0) } return ( <div> <button onClick={() => setNum(num + 1)}>{num}</button> </div> ) } export default App

4)回调函数的参数

image-20220505231210870

2、useEffect

注:是为了处理函数组件的副作用产生的

​ 函数中除了主作用的,都是副作用(理解成 一个函数只做一件事)

​ useEffect 是在DOM渲染之后执行

1)基本使用

javascript import { useState, useEffect } from 'react' export default function App() { const [count, setCount] = useState(0) /** * 1、做组件更新,副作用函数 会一直更新 * 2、useEffect里面第一个参数,是一个回调函数 */ useEffect(() => { document.title = '清理副作用' + count }) return ( <div> <button onClick={() => setCount(count + 1)}>{count}</button> </div> ) }

注:常见的副作用:

​ ① 数据请求

​ ② 手动修改dom

​ ③ localstorage等数据存储

2)执行时机

① 默认无依赖项

会在组件初始化的时候 执行一次,数据更新,他会再次执行

② 第二个参数(添加为空数组)

会在组件渲染的时候,执行一次,但是 数据变化,就不再执行

③ 给第二个参数中加入执行依赖项

javascript import { useState, useEffect } from 'react' export default function UseEffectPager() { const [count, setCount] = useState(0) /** * 1、做组件更新,副作用函数 会一直更新 * 2、useEffect里面第一个参数,是一个回调函数 */ useEffect(() => { document.title = '清理副作用' + count // count 改变 上面的就会执行 }, [count]) return ( <div> <button onClick={() => setCount(count + 1)}>{count}</button> </div> ) }

注:在useEffect里面使用了,就应该出现在依赖项数组中

3)清理useEffect的副作用

给useEffect里面写一个定时器,当你销毁组件的时候,这个定时器 并没有销毁

```javascript import { useState, useEffect } from 'react' function Com() { useEffect(() => { let time = setInterval(() => { console.log('定时器') }, 1000) // 如果 不这样写,这个计时器 就算组件销毁了,他也会以一直执行 // 这个函数 也就是做一个销毁操作 return () => { clearInterval(time) } }, []) return

组件
}

export default function UseEffectSideEffect() { const [show, setShow] = useState(true) return (

) } ```

3、useRef

1)使用步骤:

导入 useRef 函数

执行 useRef 函数并传入null,返回值为一个对象 内部有一个current属性存放拿到的dom对象(组件实例)

通过ref 绑定 要获取的元素或者组件

useRef 实在DOM渲染之前执行

2)获取页面元素

javascript import { useRef, useEffect } from 'react' export default function UseRefDom() { const dom = useRef(null) /** * 获取元素: * 变量.current */ useEffect(() => { console.log(dom.current) }) return <div ref={dom}>元素</div> }

3)获取组件

注:函数组件不可以使用useRef

​ 可调用组件中的方法

```javascript import React, { useRef, useEffect } from 'react' // 这个Com相当于 一个组件 class Com extends React.Component { fun = () => { console.log('方法') } render() { return

组件
} } export default function UseRefDom() { const dom = useRef(null) const com = useRef(null) /** * 获取元素: * 变量.current */
useEffect(() => {
    console.log(dom.current)
    com.current.fun()
})
return (
    <div>
        <Com ref={com} />
        <p ref={dom}>元素</p>
    </div>
)

} ```

4、useContext

```javascript import { createContext, useContext } from 'react' // 1、创建对象(不在同一个文件的时候,可以使用 export导出,在使用import导入) const Context = createContext()

function ComB() { // B组件中也可以使用 return (

组件B

) } function ComC() { // 3、在孙子组件中使用 const num = useContext(Context) return ( <>

组件C

{num} > ) }

export default function ComA() { return ( /** * 2、在顶层组件传值: * 使用value传 */ ) } ```

注:传入的数据是响应式的。

5、useCallback

防止因为组件重新渲染,导致方法被重新创建 ,起到缓存作用; 只有第二个参数 变化了,才重新声明一次

注:只要方法执行了,他就会 重新创建一遍组件里面的所有内容

javascript import { useCallback } from 'react' export default function UseCallback() { /** * 只有name改变后, 这个函数才会重新声明一次 * 如果传入空数组, 那么就是第一次创建后就被缓存, 如果name后期改变了,拿到的还是老的name。 * 如果不传第二个参数,每次都会重新声明一次,拿到的就是最新的name. */ const fun = useCallback(() => { console.log(name) }, [name]) return ( <div> <p onClick={fun}>UseCallback {i}</p> </div> ) }

注:与自己状态无关的时候,还用缓存的函数,与自己有关的时候 就更新函数

6、useMemo

注:useMemo 会执行传入的第一个函数

​ 类似于vue中的计算属性

```javascript import React, { useState, useEffect, useMemo } from 'react' import axios from 'axios'

export default function UseMemo() { const [mytext, setmytext] = useState('') const [cinemaList, setcinemaList] = useState([])

useEffect(() => {
    console.log(11)
    axios({
        url: 'https://m.maizuo.com/gateway?cityId=110100&ticketFlag=1&k=7406159',
        method: 'get',
        headers: {
            'X-Client-Info':
                '{"a":"3000","ch":"1002","v":"5.0.4","e":"16395416565231270166529","bc":"110100"}',

            'X-Host': 'mall.film-ticket.cinema.list'
        }
    }).then((res) => {
        console.log(res)
        setcinemaList(res.data.data.cinemas)
    })
}, [])
/**
 * useMemo 可以把第一个参数(函数) 的执行结果返回给 getCinemaList
 */
const getCinemaList = useMemo(
    () =>
        cinemaList.filter((item) =>
            item.name.toUpperCase().includes(mytext.toUpperCase())
        ),

    // 下面这俩改变,useMemo就会执行
    [cinemaList, mytext]
)

return (
    <div>
        <input
            value={mytext}
            onChange={(evt) => {
                setmytext(evt.target.value)
            }}
        />
        {getCinemaList.map((item) => (
            <dl key={item.cinemaId}>
                <dt>{item.name}</dt>
            </dl>
        ))}
    </div>
)

} ```

image-20220516001300634

7、useReducer和useContext(减少组件层级)

1)基础使用

image-20220520003619623

javascript import { useReducer } from 'react' /** * dispatch触发的方法 * @param {*} prevState 得到一个老值 * @param {*} action 下面dispatch传入的值 * 一定要有返回值 */ const reducer = (prevState, action) => { // 不能对原状态进行修改(这块也是一个深拷贝) let newPrevState = {...prevState} switch (action.type) { case 'reduce': console.log('reduce'); newPrevState.count-- return newPrevState case 'add': console.log('add'); newPrevState.count++ return newPrevState // 没匹配到返回老的状态 default: console.error('超出边界') return prevState || newPrevState.count } } // 起始状态 const intialState = { count: 0 } export default function ReduceComponentLevel() { /** * 1、useReducer里面有两个参数 * 函数 :在外部管理状态 * 函数:初始值 * 返回值: * 第一个是值(state) * 第二个是更新值(dispatch) */ const [state, dispatch] = useReducer(reducer, intialState) return ( <div> <p>减少组件层级</p> <button onClick={() => { // 他会触发reducer那个方法 dispatch({ type: 'reduce' }) }}> - </button> <p>{state.count}</p> <button onClick={() =>{ dispatch({ type: 'add' }) }}> + </button> </div> ) }

2)跨组件通讯

需求:在组件1中修改组件2、组件3的颜色

① 创建一个createContext对象,方便在不同的文件里面引入(以util.js为例)

```javascript import { createContext } from 'react' export const Context = createContext()

export const intialState = { a:'#2196f3', b:'#7bcfa4' } export function reducer(prevState,action){ console.log(prevState,action); let newPrevState = {...prevState} switch(action.type){ case 'change-two': newPrevState.a = action.value // 注意 这块返回的是对象 return newPrevState case 'change-three': newPrevState.b = action.value return newPrevState default: return prevState } } ```

② 父组件

javascript import { useReducer } from 'react' // 也可把方法 写在这 import { Context, intialState, reducer } from './utils' import Children1 from './child1' import Children2 from './child2' import Children3 from './child3' export default function ReduceComponentLevel() { /** * 1、useReducer 只能写一个,然后把变量和方法 给里面穿 * 只能在hooks中使用 */ const [state, dispatch] = useReducer(reducer, intialState) return ( <div> <p>父组件</p> <hr /> {/* 使用createContext给组件传方法和变量 */} <Context.Provider value={{ state, dispatch }}> <Children1 /> <Children2 /> <Children3 /> </Context.Provider> </div> ) }

③ 组件一

javascript import { useContext } from 'react' import { Context } from './utils' export default function Children1(){ const { dispatch } = useContext(Context) return ( <div style={{border: '1px solid red', padding: '10px'}}> <p>子组件一</p> <button onClick={() => { dispatch({ type: 'change-two', value: '#d3db22' }) }}>改变组件二</button> <button onClick={() => { dispatch({ type: 'change-three', value: "#22dbcf" }) }}>改变组件三</button> </div> ) }

④ 组件二

javascript import { useContext } from 'react' import { Context } from './utils' export default function Children2(){ const { state } = useContext(Context) return ( <div> <p style={{backgroundColor: state.a}}>子组件二 {state.a}</p> </div> ) }

⑤ 组件三

javascript import { useContext } from 'react' import { Context } from './utils' export default function Children3(){ const { state } = useContext(Context) return ( <div> <p style={{backgroundColor: state.b}}>子组件三</p> </div> ) }

202205200125933

8、案例

1)获取滚动条距离

```javascript /** * 获取滚动条距离 */ import { useState } from 'react' export default function useWindowScroll() { const [x, setX] = useState(0) const [y, setY] = useState(0)

window.addEventListener('scroll', () => {
    const top = document.documentElement.scrollTop
    setX(top)
    const left = document.documentElement.scrollLeft
    setY(left)
})
return [x, y]

} ```

2)同步修改本地数据

javascript import { useState, useEffect } from 'react' /** * 设置localStorage * @param {*} key localStorage键 * @param {*} defaultValue localStorage值 * @returns */ export default function useLocalStorage(key, defaultValue) { const [message, setMessage] = useState(defaultValue) /** * message或者key变化 都会存 */ useEffect(() => { window.localStorage.setItem(key, message) }, [key, message]) /** * message 是值 * setMessage 在外面设置值 */ return [message, setMessage] }

image-20220505225826737

3)页面初次渲染,进行数据请求

javascript import { useEffect } from 'react' export default function Axios() { useEffect(() => { console.log(1222) fetch( 'https://mock.mengxuegu.com/mock/60434bccf340b05bceda3906/practise-nuxtjs/test' ) .then((res) => res.json()) .then((res) => { console.log(res) }) }) return <div>数据请求</div> }

三、组件间通讯

1、父子组件通讯

1)父组件

javascript import FatherAndSonCom from './fatherAndSon-com' export default function FatherAndSon() { return ( <div> <FatherAndSonCom {...{ name: '李四', age: 22 }} /> <FatherAndSonCom name={'lisi'} age={21} /> </div> ) }

2)子组件

javascript export default function FatherAndSonCom(props) { const { name, age } = props return ( <div> <p>子组件</p> {name} {age} </div> ) }

image-20220510233756555

数字、字符串、布尔值、数组、对象、函数、JSX(也就是组件)

例:下面函数、JSX(也就是组件)的传递:

父组件:

javascript import FatherAndSonCom from './componentts/fatherAndSon-com' export default function FatherAndSon() { const fun = () => { console.log('传入的函数') } return ( <div> <FatherAndSonCom {...{ name: '李四', age: 22 }} fun={fun} child={<span>传入的组件</span>} /> </div> ) }

子组件:

```javascript // 可以在参数处 进行结构赋值 export default function FatherAndSonCom(props) { console.log(props) const { name, age, fun, child } = props

return (
    <div>
        <p>子组件</p>
        {name}
        {age}
        <br />
        <button onClick={fun}>触发父的函数</button>
        <br />
        {child}
    </div>
)

} ```

2、子传父

1)子组件

javascript export default function ReverseSon({ fun }) { return ( <div> <p onClick={() => fun('逆向出传值')}>子组件</p> </div> ) }

2)父组件(接受)

javascript import ReverseSon from './componentts/reverseSon' export default function ReverseFather() { const fun = (val) => { console.log(val) } return ( <div> <p>父组件</p> <ReverseSon fun={fun} /> </div> ) }

3、兄弟组件通讯

javascript /** * 真实项目中分开书写 */ import { useState } from 'react' // a组件 function A({ str }) { return <div>A组件{str}</div> } // b组件 function B({ fun }) { return <div onClick={() => fun('兄弟通讯')}>B组件</div> } // a、b的父组件 export default function Brother() { const [str, setStr] = useState('') const fun = (str) => { console.log(str) setStr(str) } return ( <> <p>父组件</p> <br /> <A str={str} /> <B fun={fun} /> </> ) }

4、跨组件通信(Context)

注:从父组件开始,可以传递到任意的子层级(忽略嵌套深度)

1)创建一个公共文件(context.js),来创建createContext(后面的数据共享,都是基于它)

javascript /** * 2、结构出来(可结构出来下面两个) * Provider 传入(他取消包括需要传入值的子组件) * Consumer 接受(接受的时候 必须是表达式(回调函数)) */ import { createContext } from 'react' export default createContext()

2)创建根组件

javascript // 1、导入 import Context from './context.js' import ContextOne from './Context-one' export default function ContextTop() { return ( <div> 父组件(也就是顶层) {/* 3、传值 */} <Context.Provider value={'跨组件通信'}> <ContextOne /> </Context.Provider> </div> ) }

3)在组件中使用(可在深层中使用)

javascript import Context from './context.js' export default function ContextOne() { return ( <div> 第一层子组件 <Context.Consumer> {(value) => <span>{value}</span>} </Context.Consumer> </div> ) }

例:

image-20220513002704757

5、 props校验

文档:https://zh-hans.reactjs.org/docs/typechecking-with-proptypes.html

1)安装第三方依赖

bash npm i --save prop-types

2)使用

① 父组件

javascript import PropsSon from './componentts/props-son' export default function Props() { return ( <div> <p>props校验</p> <PropsSon list={[1, 3, 5, 6]} /> </div> ) }

②子组件

javascript // 1、引入 import PropTypes from 'prop-types' export default function PropsSon({ list }) { console.log(list) return ( <div> <p>props校验</p> </div> ) } /** * 2、定义传入参数的类型 */ PropsSon.propTypes = { list: PropTypes.array }

3)四种常见结构:

常见类型:array、bool、func、number、object、string

React元素类型:element

必填项:isRequired

image-20220515194251188

特定的结构对象:shape({})

javascript // 特定结构的对象 传入的参数名: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number })

6、props默认值

① defaultProps

javascript /** * 子组件 */ export default function PropsSon({ num }) { console.log(num) return ( <div> <p>{num}</p> </div> ) } /** * 2、定义传入参数的类型 * 类型后面加一个 isRequired 就是必填项 */ PropsSon.defaultProps = { num: 22 }

② 函数参数,默认值(推荐)

javascript export default function PropsSon({ num = 2 }) { console.log(num) return ( <div> <p>{num}</p> </div> ) }

注:当上面两个都使用的时候,以defaultProps 传值为准。

四、Redux

注:他是js的状态管理,可以用于任何框架

文档:http://cn.redux.js.org/ (推荐)

​ http://www.codebaoku.com/it-js/it-js-246663.html ​ https://www.redux.org.cn/

​ https://github.com/camsong/redux-in-chinese

1、下载

bash npm install --save redux react-redux redux-devtools-extension redux-thunk

redux 状态管理

react-redux 就是redux将组件分为了ui组件和容器组件两类,自然我们平常写方法,页面啥的就叫ui组件,redux提供的叫容器组件,这俩组件构成了父子组件,大家记住我这说的话,下面会用到

redux-devtools-extension这个特别长的是redux官方提供的可以查看状态的ui插件,让我们在很多组件的情况下,也能知道每个组件的数据情况,非常贴心

redux-thunk这个插件可以让redux拥有使用异步操作的能力,本身redux是不支持异步操作的0

2、基础使用

1)创建一个状态管理文件(src——> redux ——> index.js)

```javascript /* * 1、引入redux */ import { createStore } from 'redux' /* * 在这定义初始值 */ const reducer = (prevState = { count: 0 }, action) => { let nweCount = {...prevState} switch (action.type) { case 'add': nweCount.count++ return nweCount case 'remove': nweCount.count-- return nweCount default: return nweCount } }

const store = createStore(reducer)

export default store ```

2)在页面中使用

注: 下面的方法 可以在不同的页面中进行操作或取值

```javascript import { useState } from 'react' import store from "../../redux/index.js" export default function ReduxTest1(){ const [num, setNum] = useState(store.getState().count || 0 ) const add = () => { store.dispatch({ type: 'add' }) } const reduce = () => { store.dispatch({ type: 'remove' })

}
// 通知
store.subscribe(()=>{
    console.log("更新了 就会触发", store.getState().count);
    setNum(store.getState().count);
})
return (
    <div>
        <p>计算器</p>
        <button onClick={add}>加</button>
        <p>{ num }</p>
        <button onClick={reduce}>减</button>        
    </div>
)

} ```

image-20220522163347055

3、reducer的使用

注:也即是 redux模块化

1)在src下创建redux文件夹:

image-20220522230451476

2)模块下的内容redux ——> reducers ——> one.js

javascript /** * 在这定义初始值 */ const OneReducer = (prevState = { count: 0 }, action) => { let nweCount = {...prevState} switch (action.type) { case 'add': nweCount.count++ return nweCount case 'remove': nweCount.count-- return nweCount default: return nweCount } } export default OneReducer

3)redux下index.js的内容

```javascript /** * 1、引入redux * combineReducers 是合并reducers 下的内容 * 可合并多个 */ import { createStore, combineReducers } from 'redux' import OneReducer from './reducers/one.js' const reducer = combineReducers({ //获取的时候,要根据这边的命名获取 OneReducer })

const store = createStore(reducer)

export default store ```

4)在页面中使用

```javascript import { useState } from 'react' import store from "@/redux/index.js" export default function ReduxTest1(){ const [num, setNum] = useState(store.getState().OneReducer.count || 0 ) const add = () => { store.dispatch({ type: 'add' }) } const reduce = () => { // dispatch 之后,所有的 Redux 都会执行一次 store.dispatch({ type: 'remove' })

}
store.subscribe(()=>{
    console.log("更新了 就会触发", store.getState().OneReducer.count);
    setNum(store.getState().OneReducer.count);
})
return (
    <div>
        <p>计算器</p>
        <button onClick={add}>加</button>
        <p>{ num }</p>
        <button onClick={reduce}>减</button>        
    </div> 
)

} ```

4、redux-thunk(中间键的使用)

注:下面的写法是错的,因为异步了

image-20220523235328036

1)给redux中加入中间键

```javascript /** * 1、引入redux * combineReducers 是合并reducer 的 * applyMiddleware 使用中间件 */ import { createStore, combineReducers, applyMiddleware } from 'redux' // 2、引入redux-thunk import reduxThunk from 'redux-thunk' import OneReducer from './reducers/one.js' import ListReducer from './reducers/list.js' const reducer = combineReducers({ //获取的时候,要根据这边的命名获取 OneReducer, ListReducer }) // 3、创建store const store = createStore(reducer, applyMiddleware(reduxThunk))

export default store ```

2)创建action,在redux——> action ——> request-list.js

注:这块也可以使用 npm i redux-promise --save

javascript import axios from 'axios' export default function requestList(){ // 这个里面 需要返回一个函数(因为外面会传递进来) // redux-thunk 会自己执行 一个方法 return (dispatch) => { axios.get('https://mock.mengxuegu.com/mock/60434bccf340b05bceda3906/practise-nuxtjs/list').then(res => { console.log(res); dispatch({ type: 'changeList', payload: res.data.list }) }) } }

3)使用

javascript import { useEffect, useState } from 'react' import store from '@/redux/index.js' import requestList from "@/redux/action/request-list.js" export default function ReduxTest1Details() { const [list, setList] = useState(store.getState().ListReducer.list) useEffect(() => { if(store.getState().ListReducer.list.length === 0) { // 4、调用action store.dispatch(requestList()) }else { console.log("store.getState().ListReducer.list 缓存"); } // 5、订阅(订阅 每次 都会走)subscribe会返回一个函数 const stopSubscribe = store.subscribe(() => { console.log(store.getState().ListReducer.list); setList(store.getState().ListReducer.list) }) // 6、在离开的时候,取消订阅 return () => { stopSubscribe() } }, []) return ( <div> <p>列表</p> { list.map((item, index) => { return <div key={index}>{item.title}</div> }) } </div> ) }

5、插件安装

https://github.com/zalmoxisus/redux-devtools-extension

插件安装:

Redux DevTools:https://github.com/reduxjs/react-redux

javascript import { compose } from 'redux' const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; /** * 如果使用了 redux-promise,写法如下: * const store = createStore(reducer,composeEnhancers(applyMiddleware(reduxThunk,reduxPromise))); */ const store = createStore(reducer, composeEnhancers(applyMiddleware(reduxThunk)));

image-20220525004808513

五、 react-redux

https://github.com/reduxjs/react-redux

https://react-redux.js.org/

1、下载

bash npm i react-redux --save

2、使用

注:这样使用 不存在异步 和 取消订阅

1)在src——> index.js中引入

javascript import React from 'react' /** * 1、使用 Provider 来包裹页面入口文件 */ import { Provider } from 'react-redux' import { createRoot } from 'react-dom/client' // 2、在这 引入redux,因为 react-redux 是基于 redux 使用的 import store from '@/redux/index' import './index.css' import App from './App' createRoot(document.getElementById('root')).render( // 3、包裹加 传入 <Provider store={store} > <App /> </Provider> )

注:@/redux 下的文件:

image-20220527004024257

2)在界面或者 组件中使用

```javascript // 4、引入connect函数 import { connect } from 'react-redux' function ReactRedux(props) { console.log(props) return (

react-redux

) }

/** * connect 的第一个参数,必须是有返回值的函数,这 函数 有个形参,可以获取到redux里面定义的内容 * */ export default connect((state) => { return { a:1, b:2, state } })(ReactRedux) ```

3)修改值

```javascript // 4、引入connect函数 import { connect } from 'react-redux' import { add, remove } from '@/redux/action/calculation' function ReactRedux(props) { const plus = () => { props.add() } const reduce = () => { props.remove() } return (

react-redux

计算器

) }

/** * connect 的第一个参数,必须是有返回值的函数,这 函数 有个形参,可以获取到redux里面定义的内容 * connect 的第er个参数: * 是给孩子(ReactRedux)用的回调函数 * */ export default connect((state) => { return { a:1, b:2, state } },{ add, remove })(ReactRedux) ```

image-20220527010638814

4)传参

image-20220527012148226

注:@/redux/action/calculation.js里面的写法: image-20220527012545330

3、 Redux 持久化

文档:https://github.com/rt2zz/redux-persist

bash npm i redux-persist

image-20220527015259986

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值