React笔记整理(超详复习材料)

本文介绍了React组件中的props、state、ref的用法,包括它们的作用、更新机制和常见应用场景。还详细讲解了JSX的数据渲染,以及如何处理不同类型的数据。此外,文章讨论了React中的组件通信,包括props传递、子组件向父组件传递值,以及使用createContext进行跨层级通信。同时,文章提到了React的生命周期方法,包括新旧版本的变化,并介绍了Redux作为状态管理工具的使用。最后,文章涵盖了ReactRouter的基本用法,如路由参数传递和编程式导航。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基础

  • 1 state
    (1)作用:用于组件保存、控制以及修改自己的状态
    (2)setState:
    作用:
    > 核心作用:页面赋值 页面渲染
    > 执行特点:每次调用都会触发render函数
    > 在不同的场景下可能是同步,也可能时异步
    setState异步情况
    > 在合成函数中异步 如onClick=“方法” 合成事件
    > 在类组件的生命周期中异步 如 componentDidMount 中
    setState同步情况
    > 在原生事件中 同步(原生事件:一个普通函数中)
    > 在定时器中同步

  • 2 props
    作用:组件通信

    传参方式
    (1)逐个传参:
    // 传递
    <Person name="jerry" age={19}  sex="男"/>
    // 接收
    class Person extends React.Component{
        render(){
            const {name,age,sex} = this.props
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{sex}</li>
                    <li>年龄:{age+1}</li>
                </ul>
            )
        }
    }
    (2)批量传参:
    // 传递
    const person = {name:'老王',age:18,sex:'女'}
    <Person {...person}/>
    // 接收
    class Person extends React.Component{
        render(){
            const {name,age,sex} = this.props
            return (
                <ul>
                    <li>姓名:{name}</li>
                    <li>性别:{sex}</li>
                    <li>年龄:{age+1}</li>
                </ul>
            )
        }
    }
    (3) this.props.children:表示所有组件的子节点
     <MyNavLink to="/about">About</MyNavLink>
     //  在MyNavLink组件中可以通过 this.props.children 获得 MyNavLink 包裹的内容值
     class MyNavLink extends Component {
        render() {
            return (
                <NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>
            )
        }
    }
  • 3 ref
    作用:用于获取DOM
    (1).创建容器 this.容器名 = React.createRef() // {current:null}
    (2).把真实 DOM 存到容器中 在虚拟DOM 中注册 ref属性 如
    (3).对容器的真实 DOM 进行操作 获取真实 DOM // this.容器名.current 就拿到了真实的 DOM
  • 4 JSX数据渲染
    (1).相关知识点
    JSX中使用 { } 来展示数据,这个{ } 也可以设置简单的JS表达式
    布尔类型的数据,React无法直接在 { }进行渲染,需要经过处理
    数组,React默认将数组拼接为字符串,需要调用map()方法进行处理
    对象Object 不能直接展示,可以转化为JSON 字符串,也可以直接调用。
    (2).渲染过程
    使用JSX声明React元素结构,这个结构就是React生成真实的DOM模板
    这个JSX模板被babel编译成React.createElement()
    由React.createElement(component, props, …children)将这些React元素渲染成虚拟DOM
    有render() 函数,讲这些虚拟DOM渲染为真实DOM,将虚拟DOM节点插入body中
    (3).示例:
        function Demo() {
            let username = "小明";
            let isMan = true;
            let age = 30;
            let arr = [10, 20, 30, 40];
            let user = {
                uname: "小红",
                uage: 22,
            };

            return (
                <div className="container">
                    <p>姓名:{username}</p>
                    <p>性别:{isMan},默认布尔类型数据,react无法直接展示</p>
                    <p>性别:{isMan ? "男" : "女"}</p>
                    <p>年龄:{age}</p>
                    <p>数组:{arr},默认React将数组拼接为字符串了</p>
                    <p>数组遍历:{arr.map((num) => num + "-")}</p>
                    <div>
                        数组遍历:
                        {arr.map((num) => (
                        <div>
                            <span>{num}</span>
                        </div>
                        ))}
                    </div>
                    <div>
                        数组遍历:
                        {arr.map((num) => {
                        return (
                            <div className="d">
                            <a href="#">{num}</a>
                            </div>
                        );
                        })}
                    </div>
                    <div>
                        对象:{JSON.stringify(user)},注意对象不能直接展示,可以转成JSON字符串
                    </div>
                    <div>
                        对象姓名:{user.uname},对象年龄:{user.uage}
                    </div>
                    </div>
                );
        }

(4).截图
在这里插入图片描述

  • 5 常用的判空方法
    (1) 判断数组为空的方法
    > arr.length=0 数组为空
    > JSON.stringify([])
    =‘[]’ 结果:true 不推荐 简单介绍一下
    (2) 判断对象为空的方法
    > Object.keys({}).length === 0 结果:true
    > JSON.stringify({})===‘{}’ 结果:true 不推荐 简单介绍一下
    (3) 数组过滤数据的常用方法
    > 可过滤掉数组中的 0 '' NaN undefined null false 
      [1,0,'',3,NaN,undefined,null,true,false].filter(Boolean) => [1, 3, true]
    > 可过滤掉数组中的 null NaN undefined
      [1,2,null,undefined,9,0,NaN,,'',true,false].filter(x=>!!x==true||x==0) => [1, 2, 9, 0, '', true, false]
      分析:
            !!:是逻辑"非非",即是在逻辑“非”的基础上再"非"一次
            !!null => false
            !!undefined => false
            !!NaN => false
            !!'' => false 
            ''==0 => true
            !!0 => false
  • 6 项目中的健壮性处理
    (1) 目的:防止因为后端返回的数据为空,前端界面拿不到值,而导致页面崩溃的问题
    (2) 解决方法:
    * 借助插件
    > 思路:判断返回值是否为空,若为空则赋初始值
    > 实现:可以借助 lodash 插件
            import _ from 'lodash'; // 组要手动安装 lodash 库
            // 在数据请求回来后进行处理,如果请求回来的数据为空则赋初始值
            const inspPlant = _.get(res.data, 'inspectionPlan', '');
            const cutNoticeList = _.get(res.data, 'cutNotice', []);
            const number = _.get(res.data, 'number', 0);
    * js原生方法
        > ?. : 可选链运算符(?.)类似于链式运算符(.),不同之处在于在引用为null或undefined时不会报错,而是短路返回undefined

在这里插入图片描述
> ?? : 只有当??左侧为null或undefined时,才会返回右侧的值,否则返回左侧值 (可以给空数据赋初始值)

                let a = null;
                let b = 1;
                // 可以用于 判断数据是否有值 如果没有值 赋初始值
                let c = a ?? b; // 1

通信

  • props 传值
    1.单项数据流
    2.示例

        import { useState } from 'react';
        const Son = (props) => {
            const {name} = props;
            return <div>{name}</div>;
        };
        const Father = () => {
            const [name, setName] = useState('Jack');
            return (
                <>
                    {/* 父组件通过属性的形式向子组件传值 */}
                    <Son name={name} />
                </>
            );
        };
        export default Father;
    
  • 子组件向父组件传值
    1.子组件调用父组件方法:将父组件的方法 以属性的形式通过 props 传递给子组件,子组件就可以调用了
    (1).示例

        import { useState } from 'react';
        const Son = ({ setCount }) => {
            return <button onClick={() => setCount(count => count + 1)}>点击+1</button>;
        };
        const Father = () => {
            const [count, setCount] = useState(0);
            return (
                <>
                    <div>计数值:{count}</div>
                        {/* 向子组件传递函数,子组件通过调用函数修改父组件数据 */}
                    <Son setCount={setCount} />
                </>
            );
        };
        export default Father;
    
  • React.createContext()发布者和消费者传值
    1.第一中方法:函数组件和类组件都实用
    (1).创建一个{Provider,Consumer} = React.createContext(‘默认值’)对象
    (2).Provider和Consumer 都是一个组件
    (3).Provider 组件中必须写一个 value 的值
    (3).Consumer必须写在Provider中 用于 订阅 Provider里的value的值
    (4).示例

        const { Provider, Consumer } = React.createContext();
        // 子类组件
        function Son() {
            return (
                <div>
                    <Consumer>
                        {/* 接收传递的数据 */}
                        {({ setCount, count }) => {
                            return <button onClick={() => setCount(count + 1)}>+</button>;
                        }}
                    </Consumer>
                </div>
            );
        }
    
        // 父类组件
        function Father() {
            const [count, setCount] = React.useState(0);
            const data = { setCount, count };
            return (
                <Provider value={data}>
                    <div>
                        {count}
                        <Son />
                    </div>
                </Provider>
            );
        }
    

    2.第二种写法 (省略了Consumer组件)类组件的写法
    (1).创建一个objName = React.createContext(‘默认值’)对象 注意:objName 是自己起的名字 里面有Provider,Consumer组件
    (2).使用 :objName.Provider
    (3).子组件中 直接定义 一个静态变量 static contextType = objName // 源码中就会 把objName.Provider里的value的值 注:this.context中 等价于 this.context = objName.Provider 里的 value的值
    (4).这种方法只能写在类组件中 因为函数组件没有this
    (5).示例

        const CounterContext = React.createContext();
        // 子类组件
        class Son extends React.Component {
            static contextType = CounterContext;
                render() {
                    //接收传递过来的参数
                    const { setState, count } = this.context;
                    return <button onClick={() => setState({ count: count + 1 })}>+</button>;
                }
        }
        // 父类组件
        class Father extends React.Component {
        state = { count: 0 };
        render() {
                // 需要传递的参数
                const data = {
                    setState: this.setState.bind(this),
                    count: this.state.count,
                };
                return (
                    <CounterContext.Provider value={data}>
                        <div>
                            {this.state.count}
                            <Son />
                        </div>
                    </CounterContext.Provider>
                );
            }
        }
    

    3.第三种写法 借助useContext可以直接拿到值
    (1).示例

        const CounterContext = React.createContext();
        // 子类组件
        function Son() {
            const { count, setCount } = React.useContext(CounterContext);
            return <button onClick={() => setCount(count + 1)}>+</button>;
        }
    
        // 父类组件
        function Father() {
            const [count, setCount] = React.useState(0);
            const data = { setCount, count };
            return (
                <CounterContext.Provider value={data}>
                    <div>
                        {count}
                        <Son />
                    </div>
                </CounterContext.Provider>
            );
        }
    
  • redux:
    1.redux是什么
    解释:(1).redux是一个专门用于做状态管理的JS库(不是react插件库)。
    (2).作用: 集中式管理react应用中多个组件共享的状态。
    2.工作流程
    在这里插入图片描述

    3.核心方法
    (1)getState()
    (2)dispatch(action)
    (3)subscribe(listener)
    4.具体编码:
    (1)store.getState()
    (2)store.dispatch({type:‘INCREMENT’, number})
    (3)store.subscribe(render)
    5.react-redux:一个react插件库,专门用来简化react应用中使用redux
    (1) 定义 UI 组件: UI 组件只做渲染 功能通过 props传递过来

        function Counter(props){
            const {value,increment,decrement} = props;
            return (
                <div>
                    <h1>{value}</h1>
                    <button onClick={increment}>+</button>
                    <button onClick={decrement}>-</button>
                </div>
            )
        }
    
    (2) 用 prototypes 规定参数类型 ->此步骤可有可无  需要下载包prop-types
    
        import  PropTypes  from "prop-types";
        Counter.prototypes = {
            value:PropTypes.number.isRequired,
            increment:PropTypes.func.isRequired
        }
    
    (3) 定义映射关系
      定义映射派发 state的 关系
      映射 state
      就规定这么写的 state是 react-redux 库里封装的 
    
        let mapStateToProps = (state)=>{
            // 返回一个对象
            return {value:state.count} 
        }
        // 简写方式
        let mapStateToProps = (state)=>({value:state.count})
    

       // 映射派发方法的 action
       let mapDispatchToProps = (dispatch)=>{
           // 返回一个对象
           return {
               //  UI组件中可以通过props解构出increment decrement方法
               increment:()=>dispatch(incrementAction()),
               decrement:()=>dispatch(decrementAction())
           }
       }
       // 简写方式 :直接等于一个对象 自动调用 dispatch
       let mapDispatchToProps = {
           increment:()=>incrementAction(),
           decrement:()=>decrementAction()
       }
    
     (4)用connect把映射关系传递进去,返回个函数 并且把 UI 组件传递进去 进行加强类似于高阶组件
    
        const Container = connect(mapStateToProps,mapDispatchToProps)(StudentForm) 
    
     (5) 在App跟组件中 并用 Provider 进行连接 store
    
        import store from './redux/store';
        import { Provider } from 'react-redux';
        class App extends React.Component{
            render(){
                return (
                    <Provider store={store}>
                        <Container/>
                    </Provider>
                    )
                }
            }
    

生命周期

类组件

一 static defaultProps
1 static defaultProps 写在类里 写法 : static defaultProps={ name:"lisi" }  
2 给 props 设置默认属性
3 给 this.props = {name:"lisi"} 赋值
二 constructor - 创建实例
1 super(props) 属性直接挂载到组件实例上 :优先使用 this.props
2 可以获取父组件传值 props
3 状态初始化 this.state = {n:0}
4 this 统一绑定 使this指向实例  this.go = this.go.bind(this)
三 conponentWillMount - 组件将要挂载
1 只会执行一次
2 此时可以获取组件实例 相关的 props 和 state
3 可以在这里通过setState 修改 state 状态数据 :但是不能触发 render 的二次渲染(因为生命周期按顺序执行,在render钩子函数没执行之前就已经修改完数据了  所以不用再次执行render)
4 不赞成 在这里初始化  this.state = {n:0} 会报出 Warning
四 componentWillReceiveProps(nextProps) 父组件传值
1 父组件 重新渲染 子组件也会重新渲染 子组件的重新渲染从componentWillReceiveProps 钩子函数开始
2 子组件的数据状态 依赖于props; 可以在此进行重新计算
3 nextProps;拿到最新的数据
    componentWillReceiveProps(nextProps){
            console.log('====componentWillReceiveProps');
            console.log('m'+':'+nextProps.m);// nextProps;拿到最新的数据
            // 子组件的数据状态 依赖于props; 可以在此进行重新计算
            let n = this.state.n
            this.setState({
                n: n + nextProps.m
            })
        }
五 shouldComponentUpdate(nextProps,nextState) 数据是否应该更改
1 核心作用 : 性能优化
2 需要有个布尔类型的返回值 
    return true -> 允许修改数据
    return false -> 不允许修改数据
3 性能优化逻辑判断 : 本质判断状态(state)和属性(props) 看是否需要重新渲染 来优化渲染频率 来提高性能
                    this.state : 当前状态
                    nextState :修改后状态
                    this.props : 当前 props 信息
                    nextProps :新传入的props信息
六 componentWillUpdate - 组件将要更新
七 render - 渲染组件
1 render : 第一次或重新进行视图渲染:解析 jsx (构建视图虚拟DOM) => 渲染成真实DOM(diff 算法) => 用户展示(更新DOM)
2 render() 方法 class 组件中唯一必须实现的方法
3 render() 也是纯函数 :state 数据不修改 则返回相同结果
4 创建虚拟 DOM , diff 算法更新 DOM 树
5 必须返回 return
八 componentDidUpdate - 组件已经更新
1 组件更新完毕 立即调用; 注意 :组件第一次加载时候不调用
2 除了第一次不调用 之后每次 更新页面都会调用一次 
九 componentDidMount - 组件第一次渲染完毕
1 componentDidMount : 第一次渲染完毕 (组件挂载完(插入真实DOM) 后立即调用)
2 只会执行一次
3 在这里去后台接口中的数据合适(ajax,axios请求的后台数据)
十 componentWillUnmount - 组件卸载
1 卸载 :资源释放 和 清理工作
2 什么时候调用(场景) : 比如清除定时器/取消订阅等
旧版本生命周期执行顺序
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    (1).constructor()
    (2).componentWillMount()
    (3).render()
    (4).componentDidMount() ==> 常用
2. 更新阶段: 由组件内部this.setSate()或父组件render触发
    (1).shouldComponentUpdate()
    (2).componentWillUpdate()
    (3).render() ==> 必须使用的一个
    (4).componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
    (1).componentWillUnmount()  ==> 常用
旧版本生命周期图

在这里插入图片描述

最新版本生命周期

新增钩子 getDerivedStateFromProps (翻译:得到一个派生的状态从 props 中)
    getDerivedStateFromProp:
     1.触发时机:挂载更新是都会触发
     2.书写形式必须以静态方法的形式进行书写
     3.接收两个参数:state props
     4.返回值是null 或者是 状态对象(状态对象:state相同的对象类似于 {count:0} )
     5.特点:state 值在任何时候都取决于 props
     6.使用场景:状态state的值完全取决于props的时候可以使用此钩子
              例子:
               class Form extends Component {
                   state = {
                       email: this.props.defaultEmail,
                       prevUserID: this.props.userID
                   };
                   static getDerivedStateFromProps(props, state) {
                       // 当前用户随时更改,
                       // 重置绑定到该用户的任何状态部分.
                       if (props.userID !== state.prevUserID) {
                           return {
                               prevUserID: props.userID,
                               email: props.defaultEmail
                           };
                       }
                       return null;
                   }

               // ...
               }
     7.可以用props里的值和初始化里state里的值做一个对比,判断以谁为主
新增钩子 getSnapshotBeforeUpdate (翻译:在更新之前获取快照)
   getSnapshotBeforeUpdate:
    1.触发时机:在最近一次渲染输出(提交到 DOM 节点)之前调用
    2.作用:它使得组件能在发生更改之前从 DOM 中捕获一些信息(例如,滚动位置)
            例如:获取之前浏览器视口的宽度 或 获取之前列表的宽度高度 传递到componentDidUpdate中处理
    3.特点:此生命周期方法的任何返回值将作为参数传递给 componentDidUpdate()
            注:componentDidUpdate(preProps,preState,snapshotValue)可以接收三个参数
    4.代码案例:
       class Demo extends React.Component{
           state = {messagesArr:[]}
           componentDidMount(){
               setInterval(() => {
                   //获取原状态
                   const {messagesArr} = this.state
                   //模拟消息
                   const messages = '消息'+ (messagesArr.length+1)
                   //更新状态
                   this.setState({messagesArr:[messages,...messagesArr]})
               }, 1000);
           }
           getSnapshotBeforeUpdate(){
               // 获更新最后一条消息前的高度
               return this.refs.list.scrollHeight
           }
           componentDidUpdate(preProps,preState,height){
               // this.refs.list.scrollHeight:更新后滚动区域的总高度
               // scrollTop:向上滚动的高度
               // height:更新最后一条消息前的高度
               this.refs.list.scrollTop += this.refs.list.scrollHeight - height
           }
           render(){
               return(
                   <div className="list" ref="list">
                       {
                           this.state.messagesArr.map((n,index)=>{
                               return <div key={index} className="messages">{n}</div>
                           })
                       }
                   </div>
               )
           }
       }

新版本生命周期执行顺序
1. 初始化阶段: 由ReactDOM.render()触发---初次渲染
    (1).constructor()
    (2).getDerivedStateFromProps 
    (3).render()
    (4).componentDidMount() ==> 常用
2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发
    (1).getDerivedStateFromProps
    (2).shouldComponentUpdate()
    (3).render()
    (4).getSnapshotBeforeUpdate
    (5).componentDidUpdate()
3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发
    (1).componentWillUnmount()  ==> 常用
新版本生命周期图

在这里插入图片描述

新旧版本生命周期差异

1.新版本即将废弃的钩子 (目前不推荐使用)
    (1).componentWillMount
    (2).componentWillReceiveProps
    (3).componentWillUpdate
2.新版本新增两个钩子
    (1).getDerivedStateFromProps
    (2).getSnapshotBeforeUpdate

函数组件

1 Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)
2 React中的副作用操作:
        发ajax请求数据获取
        设置订阅 / 启动定时器
        手动更改真实DOM
3 语法和说明: 
        useEffect(() => { 
        // 在此可以执行任何带副作用操作
        return () => { // 在组件卸载前执行
            // 在此做一些收尾工作, 比如清除定时器/取消订阅等
        }
        }, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行
    
4 可以把 useEffect Hook 看做如下三个函数的组合
        componentDidMount()
        componentDidUpdate()
        componentWillUnmount() 

路由《5&&6》

一 react-router-dom5

App组件需要 BrowserRouter 包裹
    ReactDOM.render(
        <BrowserRouter>
            <App/>
        </BrowserRouter>,
        document.getElementById('root')
    )
引入实现路由所需的组件,以及页面组件
    <ul className="nav nav-tabs">
        <li>
            <NavLink to="/home/news">News</NavLink>
        </li>
        <li>
            <NavLink to="/home/message">Message</NavLink>
        </li>
    </ul>
    {/* 注册路由 */}
    // Switch:命中一个就跳出,提高效率的东西
    <Switch>
        <Route path="/home/news" component={News}/>
        <Route path="/home/message" component={Message}/>
        <Redirect to="/home/news"/>
    </Switch>
路由传参
  • params参数
    (1) 传递params参数
        <div>
            <ul>
                {
                    messageArr.map((msgObj)=>{
                        return (
                            <li key={msgObj.id}>
                                {/* 向路由组件传递params参数 */}
                                <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link>
                            </li>
                        )
                    })
                }
            </ul>
            <hr/>
            {/* 声明接收params参数 */}
            <Route path="/home/message/detail/:id/:title" component={Detail}/>
        </div>
    
    (2) 接收params参数
        // 路由组件中的props上会有match参数中的params可以直接解构到传递的数据
        render() {
            // 接收params参数 
            // 接收到的数据结构:{id:'',title:""}
            const {id,title} = this.props.match.params
            const findResult = DetailData.find((detailObj)=>{
                return detailObj.id === id
            })
            return (
                <ul>
                    <li>ID:{id}</li>
                    <li>TITLE:{title}</li>
                    <li>CONTENT:{findResult.content}</li>
                </ul>
            )
        }
    
  • search参数
    (1) 传递search参数
       <ul>
            {
                messageArr.map((msgObj)=>{
                    return (
                        <li key={msgObj.id}>
                            {/* 向路由组件传递search参数 */}
                            <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
    
                        </li>
                    )
                })
            }
        </ul>
        <hr/>
        {/* search参数无需声明接收,正常注册路由即可 */}
        <Route path="/home/message/detail" component={Detail}/>
    
    (2) 接收search参数
       // 接收search参数
       // 接收到的数据结构:'?id=1&title=22';
    	const {search} = this.props.location;
        // parse:一个插件用于格式化接收到的search参数 => {id:'',title:""}
    	const {id,title} = qs.parse(search.slice(1))
    
    	const findResult = DetailData.find((detailObj)=>{
    		return detailObj.id === id
    	})
    	return (
    		<ul>
    			<li>ID:{id}</li>
    			<li>TITLE:{title}</li>
    			<li>CONTENT:{findResult.content}</li>
    		</ul>
    	)
    
  • state参数
    (1) 传递state参数
      <ul>
        {
            messageArr.map((msgObj)=>{
                return (
                    <li key={msgObj.id}>
                        {/* 向路由组件传递state参数 */}
                        <Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>
    
                    </li>
                )
            })
        }
    </ul>
    <hr/>
    {/* state参数无需声明接收,正常注册路由即可 */}
    <Route path="/home/message/detail" component={Detail}/>
    
    (2) 接收state参数
      render() {
    	// 接收state参数
        // 接收到的数据类型:{id:'',title:""}
    	const {id,title} = this.props.location.state || {}
    	const findResult = DetailData.find((detailObj)=>{
    		return detailObj.id === id
    	}) || {}
    	return (
    		<ul>
    			<li>ID:{id}</li>
    			<li>TITLE:{title}</li>
    			<li>CONTENT:{findResult.content}</li>
    		</ul>
    	)
    }
    
编程式路由导航
  • 编程式路由传递参数
    state = {
    	messageArr:[
    		{id:'01',title:'消息1'},
    		{id:'02',title:'消息2'},
    		{id:'03',title:'消息3'},
    	]
    }
    // history:history是路由组件中自带的属性
    replaceShow = (id,title)=>{
    	//replace跳转+携带params参数
    	//this.props.history.replace(`/home/message/detail/${id}/${title}`)
    
    	//replace跳转+携带search参数
    	// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)
    
    	//replace跳转+携带state参数
    	this.props.history.replace(`/home/message/detail`,{id,title})
    }
    
    pushShow = (id,title)=>{
    	//push跳转+携带params参数
    	// this.props.history.push(`/home/message/detail/${id}/${title}`)
    
    	//push跳转+携带search参数
    	// this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)
    
    	//push跳转+携带state参数
    	this.props.history.push(`/home/message/detail`,{id,title})
    	
    }
    back = ()=>{
    	this.props.history.goBack()
    }
    forward = ()=>{
    	this.props.history.goForward()
    }
    go = ()=>{
    	this.props.history.go(-2)
    }
    render() {
    	const {messageArr} = this.state
    	return (
    		<div>
    			<ul>
    				{
    					messageArr.map((msgObj)=>{
    						return (
    							<li key={msgObj.id}>
    								<button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>
    								<button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button>
    							</li>
    						)
    					})
    				}
    			</ul>
    			<hr/>
    			{/* 声明接收params参数 */}
    			{/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}
    
    			{/* search参数无需声明接收,正常注册路由即可 */}
    			{/* <Route path="/home/message/detail" component={Detail}/> */}
    
    			{/* state参数无需声明接收,正常注册路由即可 */}
    			<Route path="/home/message/detail" component={Detail}/>
    
    			<button onClick={this.back}>回退</button>&nbsp;
    			<button onClick={this.forward}>前进</button>&nbsp;
    			<button onClick={this.go}>go</button>
    
    		</div>
    	)
    }
    
  • 编程式路由接收参数:接收形式不变
    render() {
    	console.log(this.props);
    
    	// 接收params参数
    	// const {id,title} = this.props.match.params 
    
    	// 接收search参数
    	// const {search} = this.props.location
    	// const {id,title} = qs.parse(search.slice(1))
    
    	// 接收state参数
    	const {id,title} = this.props.location.state || {}
    
    	const findResult = DetailData.find((detailObj)=>{
    		return detailObj.id === id
    	}) || {}
    	return (
    		<ul>
    			<li>ID:{id}</li>
    			<li>TITLE:{title}</li>
    			<li>CONTENT:{findResult.content}</li>
    		</ul>
    	)
    }
    

二 react-router-dom6

App组件需要 BrowserRouter 包裹
    ReactDOM.render(
        <BrowserRouter>
            <App/>
        </BrowserRouter>,
        document.getElementById('root')
    )
引入实现路由所需的组件,以及页面组件
    import React, { Suspense } from "react";
    import { NavLink, useRoutes } from "react-router-dom";
    import Loding from "./components/Loding";

    export default function App() {
    // 此部分可以进行抽离单独写
    const element = useRoutes([{
            path: "/about",
            element: <About/> ,
        },
        {
            path: "/home",
            element: <Home/> ,
            children: [{
                    path: "news",
                    element: <News/>
                },
                {
                    path: "message",
                    element: <Message/> ,
                    children: [{
                        path: 'detile',
                        element: <Detile/>
                    }]
                }
            ]
        },
        {
            path: "/",
            element: <Navigate to = "/about"/> ,
        },
    ]);
    return (
        <div>
        <div className="row">
            <div className="col-xs-offset-2 col-xs-8">
            <div className="page-header">
                <h2>React Router Demo</h2>
            </div>
            </div>
        </div>
        <div className="row">
            <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
                <NavLink className="list-group-item " to="/about">
                    About
                </NavLink>
                <NavLink className="list-group-item" end to="/home">
                    Home
                </NavLink>
            </div>
            </div>
            <div className="col-xs-6">
            <div className="panel">
                <div className="panel-body">
                <Suspense fallback={<Loding />}>
                    {/* 写法一: Routes替换了react-router-dom5中的Switch  */}
                    {/* <Routes>
                    <Route path="/about" element={<About />} />
                    <Route path="/home" element={<Home />} />
                    <Route path="/" element={<Navigate to="/about" />} />
                    </Routes> */}
                    {/* 写法二: element需要配合useRoutes hooks来写 */}
                    {element}
                </Suspense>
                </div>
            </div>
            </div>
        </div>
        </div>
    );
    }
路由传参
  • params参数
    (1) 传递params参数
        const navigate = useNavigate();
        // 编程式路由跳转
        const show = (o) => {
            // 注意 只有 state 跳转可以写在第二个参数的配置项里,parms和search传递参数必须写在路径里
            navigate(to={`/home/message/detail/${msgObj.id}/${msgObj.title}`});
        };
        return (
            <div>
                <ul>
                    {message.map((o) => {
                    return (
                        <li key={o.id}>
                            {/* 导航式路由跳转 */}
                            <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>
                                {o.title}
                            </Link>
                            <button onClick={() => show(o)}>编程式路由跳转</button>
                        </li>
                    );
                    })}
                </ul>
                <hr />
                <Outlet />
            </div>
        );
    
    (2) 接收params参数
      // useParams : import { useParams } from "react-router-dom";获得
        const {id,title} = useParams()
        return (
            <ul>
                <li>消息编号:{id}</li>
                <li>消息标题:{title}</li>
            </ul>
        );
    
  • search参数
    (1) 传递search参数
       const navigate = useNavigate();
       // 编程式路由跳转
       const show = (o) => {
           navigate(to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`});
       };
       return (
           <div>
           <ul>
               {message.map((o) => {
                   return (
                       <li key={o.id}>
                           {/* 导航式路由跳转 */}
                           <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>
                               {o.title}
                           </Link>
                           <button onClick={() => show(o)}>编程式路由跳转</button>
                       </li>
                   );
               })}
           </ul>
           <hr />
               <Outlet />
           </div>
       );
    
    (2) 接收search参数
        // useSearchParams : import { useSearchParams } from "react-router-dom";获得
        const [search, setSearch] = useSearchParams();
        const id = search.get("id");
        const title = search.get("title");
        return (
            <ul>
                <li>消息编号:{id}</li>
                <li>消息标题:{title}</li>
            </ul>
        );
    
  • state参数
    (1) 传递state参数
        const navigate = useNavigate();
        const show = (o) => {
            // 注意 只有 state 跳转可以写在第二个参数的配置项里,parms和search传递参数必须写在路径里
            navigate("detile", {
            replace: false,
            state: {
                id: o.id,
                title: o.title,
                content: o.content,
            },
            });
        };
        return (
            <div>
            <ul>
                {message.map((o) => {
                return (
                    <li key={o.id}>
                        <Link
                            to="detile"
                            state={{ id: o.id, title: o.title, content: o.content }}
                        >
                            {o.title}
                        </Link>
                        <button onClick={() => show(o)}>编程式路由跳转</button>
                    </li>
                );
                })}
            </ul>
            <hr />
            <Outlet />
            </div>
        );
    
    (2) 接收state参数
        // useLocation : import { useLocation } from "react-router-dom";获得
        const {
            state: { id, title, content },
        } = useLocation();
        return (
            <ul>
                <li>消息编号:{id}</li>
                <li>消息标题:{title}</li>
            </ul>
        );
    

react-router-dom5与react-router-dom6的差异

  • v6中Switch名称变为Routes
    // v5
    import { HashRouter,Route,Switch } from 'react-router-dom'
        <HashRouter>
            <Switch>
                <Route path="/home" component={ Home }></Route>
                <Route path="/about" component={ About }></Route>
            </Switch>
        </HashRouter>
    // v6
    import { HashRouter,Route,Routes } from 'react-router-dom'
        //Routes替换了Switch  
        <HashRouter>
            <Routes>
                <Route path="/home" element={ <Home/> }></Route>
                <Route path="/about" element={ <About/> }></Route>
            </Routes>
        </HashRouter>
    
  • 不再支持子组件和 component , 改为 element
     // v5
     <Route path='/login' component={Login}></Route>
     // v6
     <Route path='/login' element={ <Login /> }>
    
  • v6 移除 Redirect组件,改为使用 Navigate
    // v5
    <Redirect to="/home" />
    // v6
    <Route path="/" element ={<Navigate to="/home" />} />
    
  • v6 嵌套路由改为相对匹配 : 相对v5 path 路径不用写太长了
    // v5
    <Switch>
        <Route path="/home/news" component={News}/>
        <Route path="/home/message" component={Message}/>
    </Switch>
    // v6
    <Route path='/home' element={<Home />}>
        <Route path='news' element={<News />}></Route>
        <Route path='message' element={<Message />}></Route>
    </Route>
    

react-router-dom6 的 常用的路由和 hooks

组件名作用说明
<Routes>一组路由代替原有的<Switch>,所有子路由都用基础的Router children来表示
<Route>基础路由Route是可以嵌套的
<Link>导航组件在实际页面中跳转使用
<Outlet>自适应渲染组件根据实际路由url自动选择组件
hooks作用说明
useParams返回当前参数根据路径读取参数
useNavigate返回当前路由代替原有V5中的useHistory
useOutlet返回根据路径生成的element
useLocation返回当前的location对象
useRoutes同Routes组件一样,只不过是在js中使用
useSearchParams用来匹配URL中?后边的搜索参数

拓展

  • Fragment:空标签
    1.作用:可以不用必须有一个真实的DOM根标签了
    2.示例:
        <Fragment><Fragment>
        <></>
    
  • 插槽:render props
    解:通过props传递属性,属性为函数,函数调用后返回组件,也可进行传递参数
    1.如何向组件内部动态传入带内容的结构(标签)?
    Vue中:
    使用slot技术, 也就是通过组件标签体传入结构 <A><B/></A>
    React中:
    使用children props: 通过组件标签体传入结构
    使用render props: 通过组件标签属性传入结构,而且可以携带数据,一般用render函数属性
    2.children props
        <A>
            <B>xxxx</B>
        </A>
        {this.props.children}
    
    3.render props
        <A render={(data) => <C data={data}></C>}></A>
        A组件: {this.props.render(内部state数据)}
        C组件: 读取A组件传入的数据显示 {this.props.data} 
    
    4.示例代码
        export default class Parent extends Component {
                render() {
                    return (
                        <div className="parent">
                            <h3>我是Parent组件</h3>
                            <A render={(name)=><C name={name}/>}/>
                        </div>
                    )
                }
            }
            class A extends Component {
                state = {name:'tom'}
                render() {
                    const {name} = this.state
                    return (
                        <div className="a">
                            <h3>我是A组件</h3>
                            {this.props.render(name)}
                        </div>
                    )
                }
            }
            class B extends Component {
                render() {
                    console.log('B--render');
                    return (
                        <div className="b">
                            <h3>我是B组件,{this.props.name}</h3>
                        </div>
                    )
                }
            }
    
  • useReducer : 类似于 redux
    import { useReducer } from "react";
    // import { useReducer } from "./principle";
    export default function AddCom() {
        const reducer = (state, action) => {
            switch (action.type) {
            case "add":
                return state + 1;
            case "subtraction":
                return state - 1;
            default:
                return state;
            }
        };
        const [state, dispatch] = useReducer(reducer, 0);
        return (
            <div>
                <p>{state}</p>
                { /* 派发更新 */ }
                <button onClick={() => dispatch({ type: "add" })}>+</button>
                <button onClick={() => dispatch({ type: "subtraction" })}>-</button>
            </div>
        );
    }

    // 分析:
    import { useState } from "react";
    export const useReducer = (reducer, initState) => {
        const [state, updateState] = useState(initState);
        const dispatch = (action) => {
            updateState(reducer(state, action));
        };
        return [state, dispatch];
    };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值