尚硅谷-React

—配合尚硅谷视频—

1.React初认识

(1)React是什么
react是用于构建用户 界面(视图) 的JavaScript库
也可以说react是一个将数据渲染为html视图开端的开源JS库

(2)React的功能是什么
操作Dom呈现页面

(3)为什么学React:原生JS的痛点
1 原生js操作dom效率低,繁琐

document.getElamentById('name')

2 使用js直接操作dom,浏览器会进行大量的重汇重排
3.原生js没有组件化的编码方案,代码复用率低
组件化:html、css、js都拆

(4)React的特点
1.采用组件化的模式,声明式编码,提高开发效率及组件的复用率
2.在React Native中可以使用React语法进行移动端开发
3.使用虚拟DOM+优秀的Diffing算法,尽量少的与真实的DOM的交互


2 React入门-HelloReact

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>
head>
<body>
    
    <div id="test">div>
    
    <script type="text/javascript" src="../js/react.development.js">script>
    
    <script type="text/javascript" src="../js/react-dom.development.js">script>
    
    <script type="text/javascript" src="../js/babel.min.js">script>

    
    <script type="text/babel">
        //1.创建虚拟dom
        const Vdom=<h1>hello-react</h1>
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
    script>
body>
html>

3 为什么用jsx?对比虚拟dom的两种创建方式

//创建内容展示:
<h1 id="title">
      <span id="thespan">hello-reactspan>
h1>

(1)使用jsx创建虚拟dom

    <script type="text/babel">
        //1.创建虚拟dom
        const Vdom=<h1 id="title"><span id="thespan">hello-react</span></h1>
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
    script>

(2)使用js创建虚拟dom
api:React.createElement(标签名,标签属性,标签内容):用于创建虚拟dom

    <script type="text/javascript">
        // 创建虚拟dom React.createElement(标签名,标签属性,标签内容)
        const Vdom=React.createElement('h1',{id:'title'},React.createElement('span',{id:'thespan'},'hello-react'))
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
    script>

(3)结论
jsx是js在react方面的语法糖


4.什么是虚拟dom

它其实是一个Object对象

关于虚拟dom

  • 虚拟dom本质是object类型的对象
  • 虚拟dom相比于真实dom“轻”(虚拟dom的属性较少),因为虚拟dom是react内部在用,所以无需真实dom那么多的属性
  • 虚拟dom最终会被react转化为真实dom,呈现在页面中
<script type="text/babel">
        //1.创建虚拟dom
        const Vdom=<h1><span>hello-react</span></h1>
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))
        //创建真实际dom
        const Tdom=document.getElementById('test')
        console.log('虚拟dom',Vdom)
        console.log('真实dom',Tdom)
script>

请添加图片描述


5.JSX的语法规则

5.1 关于JSX

  • jsx全称JavaScript XML
  • 是react定义的一种类似于XML的JS扩展语法JS+XML
  • 作用是用来简化创建虚拟DOM
  • 标签名任意:HTML标签或者其他标签

XML 早期用于存储和传输数据
< student >
< name> 18< /name >

5.2 JSX语法规则

  • 定义虚拟dom的时候不要写引号
  • 标签中混入JS表达式时要用{ }

注意区分:js表达式和js语句

  1. 表达式:表达式产生一个值可以放在任何一个需要值的地方。
    例如:a,a+b,add(1),arr.map(),function(){}
  2. 语句: 例如:if(){},for(){},switch(){}
    <script type="text/babel">
        const myId="guigu"
        const myName="lihua"
        //1.创建虚拟dom
        const Vdom2=(
            <h2 id="{myId}">
                <span>{myName}</span>
            </h2>
        )
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom2,document.getElementById('test'))
    </script>
  • 样式的类名不要用class,要用className
        const Vdom2=(
            <h2 id="{myId}" className="title">
                <span>{myName}</span>
            </h2>
        )
  • 内联样式要用 style={{key:'value’,key2:‘value2’}} 的形式来写
        const Vdom2=(
            <h2 id="{myId}" className="title">
                <span style={{color:'white',fontSize:'40px'}}>{myName}</span>
            </h2>
        )
  • 虚拟dom必须只有一个根标签
  • 标签必须闭合
     <input type="text"/> 
     <input type="text"></input>
  • 标签首字母:
    (1)若小写字母开头,标签转化为html中同名标签,如果html找不到此标签-----报错
    (2)若大写字母开头,React就去渲染对应的组件,若组件没有定义-----报错
  • render中写注释的格式:
 {/*{this.input1=currentNode;console.log(currentNode)}}/> */}

6 React中的数据循环遍历

    <script type="text/babel">
        // 模拟数据
        const title="前端框架列表"
        const data=['angular','vue','react']
        //1.创建虚拟dom
        const Vdom=(
            <div>
                <h2>{title}</h2>
                <ul>
                    {
                        data.map((item,index)=>{
                            return <li key={index}>{item}</li>
                        })
                    }
                </ul>
            </div>
        )
        //2.渲染虚拟dom到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(Vdom,document.getElementById('test'))

7 模块与组件、模块化与组件化的理解

(1)模块:

  • 理解:向外提供特定功能的js程序,一般就是一个js文件。
  • 为什么要拆分成模块:随着业务逻辑的增加,代码越来越复杂
  • 作用:复用js,简化js的编写,提高js运行效率
  • 模块化:当应用的js都以模块来编写,这个应用就是一个模块化的应用

(2)组件:

  • 理解:用来实现局部功能效果的代码和资源的集合 包括js、html、css等
  • 为什么:一个界面的功能复杂
  • 作用:复用编码,简化项目代码的编写,提高项目运行效率
  • 组件化:当应用是以多组件的方式实现,这个应用就是一个组件化的应用

8 React组件

8.1 分类

名称 描述
函数式组件 用函数定义的组件,适用于简单组件的定义
类式组件 用类定义的组件,适用于复杂组件的定义

8.2 函数式组件

    <script type="text/babel">
        //1.创建函数式组件
        function Demo(){
            //此处的this是undefined 因为bable翻译后开启了严格模式
            return <h2>我是函数定义的简单组件,适用于简单组件的定义</h2>
        }
        //2.渲染组件到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

执行ReactDOM.render( 1.react解析了组件标签,找到了Demo组件
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟dom转化为真实dom
3.将dom呈现在页面中。

8.3 类式组件

    <script type="text/babel">
        //1.创建类式组件 必须继承react中内置的类 React.Component
        class MyComponent extends React.Component{
            render(){
                return <h2>我是定义的类式组件</h2>
            }
        }
        //2.渲染组件到页面 ReactDom.render(虚拟DOM,容器)
        ReactDOM.render(<MyComponent/>,document.getElementById('test'))
    </script>

ReactDOM.render(< MyComponent/>,document.getElementById(‘xxxx’))之后发生了什么?
1.MyComponent解析了组件标签,找到了MyComponent组件
2.发现组件是类定义的,随后new出来该类的实例,并通过该实例调用到原型上的rander方法
3.将rander返回的虚拟dom转化为真实dom
4.将dom渲染在页面中

9 组件三大属性之---- state

名称 描述
简单组件 没有state的
复杂组件 有state的

(1)实例

script type="text/babel">
        class Weather extends React.Component{
            constructor(props){
                super(props) 
                // 初始化状态
                this.state={isHot:true}
                // 将原型对象上的方法转为实例自身上的方法
                this.changeWeather=this.changeWeather.bind(this)
            }
            changeWeather(){
                //1.类中的方法默认开启严格模式
                //2. 由于changeWeather是作为onclick事件的回调 不是通过实例调用的而是直接调用
                //结论:changeWeather中的this是undefined

                // 严重注意:状态不可直接跟改 要使用内置的api
                this.setState({isHot:!this.state.isHot})
            }
            render(){
                return <h2 id='title' onClick={this.changeWeather}>今天天气很{this.state.isHot?'炎热':'凉爽'}</h2>
            }
        }
        ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>

(2)使用this.setState更新state

这里的更新是一种合并不会影响其他属性,而不是覆盖

this.setState({isHot:!this.state.isHot})

(3)state的简写方式
主要运用原理:类中可以直接写赋值语句,含义是给car的实例对象添加一个属性。

    <script type="text/babel">
        class Weather extends React.Component{
            constructor(props){
                super(props)
            }
            // 类中可以直接写赋值语句 含义是给类的实例添加一个属性
            state={isHot:true}
            changeWeather=()=>{
                this.setState({isHot:!this.state.isHot})
            }
            render(){
                return <h2 id='title' onClick={this.changeWeather}>今天天气很{this.state.isHot?'炎热':'凉爽'}</h2>
            }
        }
        ReactDOM.render(<Weather/>,document.getElementById('test'))
    </script>

(4)总结state

  • 组件中rander方法中的this为组件的实例对象

  • 组件自定义的方法中this为undefined,如何解决?
    (1)使用bind强制改变指向
    (2)使用箭头函数

  • 状态数据不能直接修改,要用this.setState({})

10 组件三大属性之-----prop

(1)基本用法

    <script type="text/babel">
        class Person extends React.Component{
            constructor(props){
                super(props)
            }
            render(){
                console.log(this)
                return(
                <ul>
                  <li>姓名{this.props.name}</li>
                  <li>年龄{this.props.age}</li>    
                  <li>性别{this.props.sex}</li>        
                </ul>
                ) 
            }
        }
        ReactDOM.render(<Person name="tom" age="18" sex="man"/>,document.getElementById('test'))
    </script>

(2)对标签属性进行限制和设置默认值

    <!-- 引入prop-types用于对组件标签属性进行限制 -->
    <script type="text/javascript" src="../js/prop-types.js"></script>
    <script type="text/babel">
        class Person extends React.Component{
            constructor(props){
                super(props)
            }
            render(){
                const {name,age,sex} = this.props
                return(
                <ul>
                  <li>姓名{name}</li>
                  <li>年龄{age}</li>    
                  <li>性别{sex}</li>        
                </ul>
                ) 
            }
        }
        // 指定参数类型
        Person.propTypes={
            name:PropTypes.string.isRequired,
            sex:PropTypes.string,
            age:PropTypes.number,
            fun:PropTypes.func,
        }
        // 指定默认值
        Person.defaultProps={
            sex:'男',
            age:18
        }
        const p={name:'tom',age:"18",sex:'man'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test'))
        //会出现报错信息:Warning: Failed prop type: Invalid prop `age` of type `string` supplied to `Person`, expected `number`.in Person

(3)props值是只读的

            render(){
                this.props.age=19 //报错
                const {name,age,sex} = this.props
                return(
                <ul>
                  <li>姓名{name}</li>
                  <li>年龄{age}</li>    
                  <li>性别{sex}</li>        
                </ul>
                ) 
            }

(4)props简写形式
将propTypes和defaultProps放在类定义的内部

    <script type="text/babel">
        // 创建组件
        class Person extends React.Component{
            // 指定类型
            static propTypes={
               name:PropTypes.string.isRequired,
               sex:PropTypes.string,
               age:PropTypes.number,
               fun:PropTypes.func,
            }
            // 设置默认值
            static defaultProps={
               sex:'男',
               age:18
            }
            state={secname:'lihua'}
            render(){
                const {name,age,sex} = this.props
                return(
                <ul>
                  <li>姓名{name}</li>
                  <li>年龄{age}</li>    
                  <li>性别{sex}</li>
                  <li>state:{this.state.secname}</li>     
                </ul>
                ) 
            }
        }
        const p={name:'tom',age:18,sex:'man'}
        ReactDOM.render(<Person {...p}/>,document.getElementById('test'))
    </script>

(5)函数式组件中使用props

    <script type="text/babel">
        //1.创建函数式组件
        function MyComponent(props){
            return <h2 className='title'>我是{props.name}</h2>
        }
        //2.渲染组件到页面 ReactDom.render(虚拟DOM,容器)
        const p={name:'tom',age:18,sex:'man'}
        ReactDOM.render(<MyComponent {...p}/>,document.getElementById('test'))
    </script>

11 类中的构造器

类中的构造器是否接收props,是否传递给super取决于:是否希望在构造器中通过this访问props
构造器可以直接删除

constructor(props){
     super(props)
}

12 组件的三大属性-----ref

(1)字符串形式的ref
不推荐
因为string类型的ref存在效率的问题。

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            //展示输入框的数据
            showData = ()=>{
                alert(this.refs.input1.value)
            }
            showData2 = ()=>{
                alert(this.refs.input2.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="点击按钮提示数据"  ref="input1"/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2} ref="input2"/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

(2)回调函数形式的ref

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            //展示输入框的数据
            showData = ()=>{
                alert(this.input1.value)
            }
            showData2 = ()=>{
                alert(this.input2.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="点击按钮提示数据"  ref={currentNode=>this.input1=currentNode}/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <input type="text" placeholder="失去焦点提示数据" onBlur={this.showData2}  ref={currentNode=>this.input2=currentNode}/>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

(3)createRef API形式的ref

React.createRef:调用后可以返回一个容器,该容器可以存储被Ref所标识的节点
该容器是专人专用的

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            input1 = React.createRef()
            input2 = React.createRef()
            showData = ()=>{
                alert(this.input1.current.value)
            }
            showData2 = ()=>{
                alert(this.input2.current.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="点击按钮提示数据"  ref={this.input1 }/>&nbsp;
                        <input type="text" placeholder="点击按钮提示数据"  ref={this.input2 }/>&nbsp;
                        <button onClick={this.showData}>点我提示左侧数据</button>&nbsp;
                        <button onClick={this.showData2}>点我提示左侧数据</button>&nbsp;
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

13 React中的事件处理

1.通过onXxx属性指定事件处理函数(注意大小写)
(1)React使用的是自定义事件,而不是原生dom事件------为了更好的兼容性
(2)React中的事件是通过事件委托方式处理的(委托给组件最外层的元素----------为了高效
2. 通过event.target得到发生事件的dom元素
3.不要过度的使用ref

    <script type="text/babel">
        //创建组件
        class Demo extends React.Component{
            showData2 = (event)=>{
                alert(event.target.value)
            }
            render(){
                return (
                    <div>
                        <input type="text" placeholder="失去焦点提示数据"   onBlur={this.showData2}/>
                    </div>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Demo/>,document.getElementById('test'))
    </script>

14 React中的收集表单数据

14.1 非受控组件

页面中输入类的dom 是现用现取的状态 就是非受控组件

    <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                let {username,password} = this
                alert(`你输入的用户名是${username.value},你输入的密码是${password.value}`)
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" ref={c=>this.username=c}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" ref={c=>this.password=c}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

14.2 受控组件

受控组件:页面中所有输入类的dom随着输入,输入的值被维护在状态【state】中。

    <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            state={username:'',password:''}
            // 提交
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                alert(`你输入的用户名是${this.state.username},你输入的密码是${this.state.password}`)
            }
            // 保存用户名在状态中
            saveUsername=(event)=>{
               this.setState({username:event.target.value})
            }
            // 保存密码在状态中
            savePassword=(event)=>{
               this.setState({password:event.target.value})
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" onChange={this.saveUsername}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" onChange={this.savePassword}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

15 高阶函数,函数的柯里化

高阶函数: 如果一个函数符合下面两个规范中的任何一个,那这个函数就是高阶函数。
(1)若a函数,接收的参数是一个函数,那么a函数就可以称为高阶函数
(2)若a函数调用的返回值为一个函数,那么a就可以称为高阶函数
常见的高阶函数 promise、setTimeOut、arr.map…
函数柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

        function add(a){
            return (b)=>{
                return (c)=>{
                    return a+b+c
                }
            }
        }
        let a = add(1)(2)(3)
        console.log(a)
    <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            state={username:'',password:''}
            // 提交
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                alert(`你输入的用户名是${this.state.username},你输入的密码是${this.state.password}`)
            }
            // 保存表单数据到状态中
            saveFomeData=(type)=>{
                return (event)=>{
                    // 使用[]来获取变量 因为直接写type会把type当做字符串而不是变量
                    this.setState({[type]:event.target.value})
                }
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" onChange={this.saveFomeData('username')}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" onChange={this.saveFomeData('password')}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>
 saveUserinfo=(type)=>{
      return (event)=>{
          this.setState({[type]:event.target.value})
      }
}
注意:这里为什么是return这个函数 
是因为调用的时候onChange={this.saveFomeData('password')}的涵义是
将this.saveFomeData('password')的返回值作为onChange的回调。

注意:这里的[type]:event.target.value属性名要用[]来包裹(用于读取变量),否则会被视为普通字符串。

不用柯里化

onChange={(event)=>this.saveFomeData(‘username’,event)}
onchange需要函数作为回调,就利用箭头函数来传递一个函数作为回调。

  <script type="text/babel">
        //创建组件
        class Login extends React.Component{
            state={username:'',password:''}
            // 提交
            handleSubmit=(event)=>{
                event.preventDefault()//阻止默认事件
                alert(`你输入的用户名是${this.state.username},你输入的密码是${this.state.password}`)
            }
            // 保存表单数据到状态中
            saveFomeData=(type,event)=>{
                this.setState({[type]:event.target.value})
            }
            render(){
                return(
                    <form action="" onSubmit={this.handleSubmit}>
                        用户名:<input type="text"  id="" placeholder="请输入用户名" name="username" onChange={(event)=>this.saveFomeData('username',event)}/>
                        密码:<input type="password"  id="" placeholder="请输入密码" name="password" onChange={(event)=>this.saveFomeData('password',event)}/>
                        <button>登录</button>
                    </form>
                )
            }
        }
        //渲染组件到页面
        ReactDOM.render(<Login/>,document.getElementById('test'))
    </script>

16 React的生命周期

(2)React生命周期(旧版本)
尚硅谷-React_第1张图片
尚硅谷-React_第2张图片

(2)React生命周期(新版本)
尚硅谷-React_第3张图片

(16-1):componentDidMount

调动时机:组件挂载完毕时调用,一般用于做一些初始化的事情。(开启定时器,发送网络请求、订阅消息)

    <script type="text/babel">
        //创建组件
        class Life extends React.Component{
            state={opacity:1}
           //使组件从页面移除
           distory=()=>{
               ReactDOM.unmountComponentAtNode(document.getElementById('test'))
           }
           // 调动时机:组件挂载完毕时调用
           componentDidMount(){
                setInterval(()=>{
                   let {opacity} = this.state
                   opacity -= 0.1
                   if(opacity <= 0) opacity=1
                   this.setState({opacity})
               },200)
           }
           render(){
               return(
                   <div>
                      <h2 style={{opacity:this.state.opacity}}>ReactReactReactReact</h2>
                      <button onClick={this.distory}>消失</button>
                    </div>
               )
           }
        }
        //渲染组件到页面
        ReactDOM.render(<Life/>,document.getElementById('test'))
    </script>

(16-2):componentWillUnmount

调用时机:组件将要被卸载时调用,一般做一些收尾的事(关闭定时器、取消订阅消息)

卸载组件:this.distory

    <script type="text/babel">
        //创建组件
        class Life extends React.Component{
            state={opacity:1}
           //使组件从页面移除
           distory=()=>{
               ReactDOM.unmountComponentAtNode(document.getElementById('test'))
           }
           // 调用时机:组件将要被卸载时调用
           componentWillUnmount(){
            clearInterval(this.timer)
           }
           render(){
               return(
                   <div>
                      <h2 style={{opacity:this.state.opacity}}>ReactReactReactReact</h2>
                      <button onClick={this.distory}>消失</button>
                    </div>
               )
           }
        }
        //渲染组件到页面
        ReactDOM.render(<Life/>,document.getElementById('test'))

(16-3):新版本和旧版本的区别

1.新版本中要用

UNSAFE_componentWillReceiveProps
UNSAFE_componentWillMount
UNSAFE_componentWillUpdate

2 新版本出现两个新的钩子函数
使用的情况比较少

getDerivedStateFromProps
getSnapshotBeforeUpdate

getDerivedStateFromProps:从props中得到一个派生的状态,若state中的值在任何时候都取决于props中时使用【一般不使用】

// getDerivedStateFromProps
//返回值为一个对象:state中的同名值被props替代且不能被修改
//返回值为null:没有影响
//返回值为props:state中的同名值被props替代且不能被修改
static getDerivedStateFromProps(props,state){
      console.log('Count-getDerivedStateFromProps',props,state)
      return props
}

getSnapshotBeforeUpdate:在更新之前获取快照 【不常用】

//getSnapshotBeforeUpdate
getSnapshotBeforeUpdate(){
   console.log('Count-getSnapshotBeforeUpdate')
    return 'weijiamin'//会被componentDidUpdate接收
}

17 React脚手架

步骤
1全局安装: npm install -g create-react-app/npm i create-react-app -g
2.切换到想创建项目的目录,使用命令:create-react-app hello-react
3.进入项目文件夹:cd hello-react
4 启动项目:npm start
5ts+react :create react-app xxx --template typescript
尚硅谷-React_第4张图片

快捷键
rcc:快速生成React类式组件框架
rfc:快速生成React函数式组件

18 父子组件传值

1.父-子
父组件

export default class App extends Component {
  state = {
    todos: [
      { id: "001", name: "吃饭" ,done:true},
      { id: "002", name: "睡觉" ,done:false},
      { id: "003", name: "上班" ,done:false},
      { id: "004", name: "打游戏" ,done:false},
    ],
  };
  render() {
    return (
        <List todos={this.state.todos} />
    );
  }
}

子组件

export default class List extends Component {
  render() {
    return (
      <ul className="todo-main">
        {
          this.props.todos.map((item,index)=>{
            return <Item key={item.id} todo={item}/>
          })
        }
      </ul>
    );
  }
}

2.子-父:实际是方法的传递
父组件

export default class App extends Component {
  a=(data)=>{
    console.log('父组件接收到的值',data)
  }
  render() {
    return (
          <Header a={this.a}/>
    );
  }
}

子组件

export default class Header extends Component {
  handleKeyUp=(event)=>{
    this.props.a(event.target.value)
  }
  render() {
    return (
        <input type="text" placeholder="请输入你的任务名称,按回车键确认"  onKeyUp={this.handleKeyUp}/>
    );
  }
}

注意:当子组件调用的父组件的函数中使用到了this相关的数据的时候,绑定方法要手动指定this指向否则this会默认指向当前组件

父组件:
export default class TestA extends Component {
    state={
        list:['1','2','3'],
    }
    changeItem(index){
        console.log(index,this.state)
        let list = this.state.list
        list[index] = '你好'
        this.setState({list:list})
    }
    render() {
        const _this = this
        return (
        <div>
            {this.state.list.map((item,index)=>{
                return <div key={index} className='list-item' onClick={()=>{this.changeItem(index)}}>{item}</div>
            })}
            <div>这里调用子组件</div>
            <TestB list={this.state.list} changeItem={_this.changeItem.bind(this)}></TestB>
        </div>
        )
    }
}
子组件:
export default class TestB extends Component {
  changeItemByParent(index){
    this.props.changeItem(index)
  }
  render() {
    return (
      <div>
        {this.props.list.map((item,index)=>{
           return <div key={index} onClick={()=>{this.changeItemByParent(index)}}>{item}</div>
        })}
      </div>
    )
  }
}

参考:react 子组件调用父组件的方法时,this指向子组件的问题

19 消息订阅与发布机制-兄弟组件传值

订阅消息:1.消息名 2.发布消息
下载:npm i pubsub-js --save
使用:
(1)import PubSub from ‘pubsub-js’ 引入
(2)let token=PubSub.subscribe(‘delete’,function(data){} 订阅 【token相对于订阅的id】
(3)PubSub.publish(‘delete’,data) 发布消息 【参数一:消息名;参数二:携带的数据】
(4)PubSub.unsubscribe(‘token’) 取消订阅 【传入订阅id取消订阅】

组件1:订阅消息

  //初始化状态
  state={
    users:[],
    isFirst:true,//是否为第一次打开页面
    isLoading:false,//是否为加载状态
    err:''//存储请求相关的错误信息
  }
 // 接收到两个参数 参数1:消息的名称 参数2:消息传递的值
  componentDidMount(){
    this.token=PubSub.subscribe('message',(msg,data)=>{
      this.setState(data)
    })
  }
  //取消订阅
  componentWillUnmount(){
    PubSub.unsubscribe(this.token)
  }

组件2:发布消息

search=()=>{
    //获取用户输入
    let keyword = this.keyWordNode.current.value
    // 发送请求前通知List更新状态
    PubSub.publish('message',{isFirst:false,isLoading:true})
    //发送网络请求
    axios.get(`http://localhost:3000/api1/search/users?q=${keyword}`).then(
      res=>{
        //请求成功后通知List更新状态
        PubSub.publish('message',{isLoading:false,users:res.data.items})
      },
      reason=>{
        //请求失败后通知更新List状态
        PubSub.publish('message',{isLoading:false,err:reason.message})
      }
    )
  }

20 -React 配置代理

方式1
方式:修改package.josn文件
注意:要重新启动脚手架
尚硅谷-React_第5张图片
发送请求:

    axios.get('http://localhost:3000/students').then(
      res=>{console.log('成功',res.data)},
      error=>{console.log('失败')}
    )

3000端口 要向5000端口发送请求 修改代理之后发送请求的端口改为3000
缺点: 只能配置一个代理地址,当服务端地址改变后失效

方式2
在src文件夹下面添加setupProxy.js文件

const { createProxyMiddleware } = require("http-proxy-middleware")
module.exports = function (app) {
  app.use(
    createProxyMiddleware("/api1",{
      target: "http://localhost:5000", //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: { "^/api1": "" }, //重写请求路径
    }),
    createProxyMiddleware("/api2",{
      target: "http://localhost:5001", //配置转发目标地址(能返回数据的服务器地址)
      changeOrigin: true, //控制服务器接收到的请求头中host字段的值
      pathRewrite: { "^/api2": "" }, 
    })
  )
}

发送请求

  getStudentInfo = () => {
    axios.get("http://localhost:3000/api1/students").then(
      res => {
        console.log("成功", res.data);
      },
      error => {
        console.log("失败");
      }
    );
  };

21 fatch发送网络请求

没看

22 React路由

1.SPA的理解

1.单页web应用 (single web page SPA)
2.整个应用只有一个完整的页面
3.点击页面中的链接不会刷新页面,只会做页面的局部更新
4.数据都需要通过ajax请求获取,并在前端异步呈现

2.路由的理解

一个路由就是一个映射关系(key:value)
key为路径,value可能是function或者component

注册路由 import {Link,Route } from "react-router-dom";

尚硅谷-React_第6张图片
注意:要用BrowserRouter包裹 < App/> ,因为都需要包裹直接在index.js中操作

尚硅谷-React_第7张图片

Link,Route都是路由组件 为了规范About,Home都应该放在pages文件夹中 而不是components
所以组件可以分为 一般组件路由组件
区别:
1 路由组件放在pages文件中,一般组件放在component文件中。
2 路由组件需要通过路由匹配调用 一般组件直接使用
3 路由组件默认会收到props,一般组件只有手动传值才会有尚硅谷-React_第8张图片

22 封装NavLike

组件

import React, { Component } from 'react'
import {NavLink } from "react-router-dom";

export default class MyNavLink extends Component {
  render() {
    return (
        <NavLink className="list-group-item" activeClassName="active_nav" {...this.props}></NavLink>
    )
  }
}

NavLink可以通过指定activeClassName来指定active时的样式
这里的标签体内容【如about】会被接收到this.props.children
接收的时候直接用 {…this.props} 来接收 可以将属性直接放在标签中

使用

 <MyNavLink  to="/about">aboutMyNavLink>
 <MyNavLink  to="/home">homeMyNavLink>

23 使用Switch组件控制路由匹配

作用:当匹配到一个路由后就中断匹配,提高匹配效率。

import {Route,Switch } from "react-router-dom";
//当路由为/home时 结果:只显示Home组件而不显示Test组件。
<Switch>
     <Route path="/about" component={About}/>
     <Route path="/home" component={Home}/>
     <Route path="/home" component={Test}/>
Switch>

24 多层级路由刷新后样式丢失问题解决

        <div className="row">
          <div className="col-xs-2 col-xs-offset-2">
            <div className="list-group">
                <MyNavLink  to="/weijiamin/about">aboutMyNavLink>
                <MyNavLink  to="/weijiamin/home">homeMyNavLink>
            div>
          div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                  {/* 注册路由 */}
                  <Switch>
                      <Route path="/weijiamin/about" component={About}/>
                      <Route path="/weijiamin/home" component={Home}/>
                  Switch>
              div>
            div>
          div>
        div>

方法1.修改pubilc/index.js中的样式引入

   //不加./
    <link rel="stylesheet" href="/css/bootstrap.css">

方法2使用 %PUBLIC_URL%

    <link rel="stylesheet" href="%PUBLIC_URL%/css/bootstrap.css">

方法3 使用 HashRouter 【不常用】

import { HashRouter} from "react-router-dom";
//渲染App组件到页面
ReactDOM.render(
   <HashRouter>
      <App/>
    </HashRouter>
,document.getElementById('root')
)

25 路由匹配规则

1.默认是模糊匹配(最左匹配)
可以匹配的情况

<MyNavLink  to="/about/weijiamin">aboutMyNavLink>
<Route path="/about" component={About}/>
<MyNavLink  to="/about">aboutMyNavLink>
<Route path="/about" component={About}/>

2.开启精准匹配(全部都要相等)
exact
使用的原则:一般不使用

<MyNavLink  to="/weijiamin/about">aboutMyNavLink>
<Route exact path="/weijiamin/about" component={About}/>

26 设置默认的路由,路由重定向-Redirect

使用Redirect【重定向】

一般写在所有路由注册的最下方,当所有路由都无法匹配的时候,跳转到Redirect指定的路由 一般用于编写默认路由

 {/* 注册路由 */}
 <Switch>
       <Route path="/about" component={About}/>
       <Route path="/home" component={Home}/>
       <Redirect to="/home">Redirect>
Switch>

27 嵌套路由

export default class Home extends Component {
  render() {
    return (
      <div>
        <ul className="nav nav-tabs">
          <li>
              <MyNavLink to="/home/news">news</MyNavLink>
          </li>
          <li>
              <MyNavLink to="/home/message">message</MyNavLink>
          </li>
        </ul>
        {/* 注册路由 */}
        <Switch>
            <Route path="/home/news" component={News}/>
            <Route path="/home/message" component={Message}/>
            <Redirect to="/home/message"></Redirect>
        </Switch>
      </div>
    )
  }

注意:

  1. 注册子路由的时候要写上父路由的path值。
  2. 路由的匹配是按照注册路由的顺序执行的。

28 路由传参

(28-1)路由传递params参数

{/* 像路由组件传递params参数 */}
<Link to={`/home/message/detail/${item.id}/${item.title}`}>{item.title}Link>
{/* 声明接收params参数 */}
<Route path="/home/message/detail/:id/:title" component={Detail}/>

组件接收参数
尚硅谷-React_第9张图片

(28-2)路由传递search参数

像路由组件传递search参数 
<Link to={`/home/message/detail?id=${item.id}&title=${item.title}`}>{item.title}</Link>
{/* search参数无需声明接收 正常声明即可*/}
<Route path="/home/message/detail" component={Detail}/>
import qs from 'qs'
接收saerch参数
let {search} = this.props.location
let {id,title}= qs.parse(search.slice(1))

(28-3)路由接收state参数

区别:参数没有暴露在地址栏中。

像路由组件传递state参数
<Link to={{pathname:'/home/message/detail',state:{id:item.id,title:item.title}}}>{item.title}</Link>
{/* state参数无需声明接收 正常声明即可*/}
<Route path="/home/message/detail" component={Detail}/>
接收state参数
const {state}=this.props.location

虽然没有在地址栏中但是刷新state参数也不会消失
因为history对象一直记录了
但是清除内存后会消失
解决:
const {state}=this.props.location||{}

29 路由的push与replace

1.push(默认状态)

路由(栈)
http://localhost:3001/home/message/detail
http://localhost:3001/home/message
http://localhost:3001/home
http://localhost:3001/about

2.replace模式
开启replace模式

 <Link replace={true} to="/home/message/detail">{item.title}Link>
路由(栈)
http://localhost:3001/home/message/detail
http://localhost:3001/home/message
http://localhost:3001/home
http://localhost:3001/about

30 编程式路由导航

设置按钮

<button onClick={()=>{this.showReplace(item.id,item.title)}}>replace查看</button>
<button onClick={()=>{this.showPush(item.id,item.title)}}>push查看</button>
  showReplace=(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})
  }
  
  showPush=(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})
  }

注意:无论是哪种方式传递,声明接收参数和接收参数的要与之匹配

31 编程式路由前进和后退

<button onClick={this.back}>回退button>
<button onClick={this.forward}>前进button>
<button onClick={this.goByNum}>前进或后退几部button>
  back=()=>{
    this.props.history.goBack()
  }
  forward=()=>{
    this.props.history.goForward()
  }
  goByNum=()=>{
    // 正数为前进 负数为后退
    this.props.history.go(-2)
  }

32 withRouter的使用

withRouter可以加工一般组件,使一般组件具备路由组件特有的api
withRouter返回的是一个新组件

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'

class Header extends Component {
  back=()=>{
    this.props.history.goBack()
  }
  forward=()=>{
    this.props.history.goForward()
  }
  goByNum=()=>{
    // 正数为前进 负数为后退
    this.props.history.go(-2)
  }
  render() {
    return (
        <div className="page-header">
          <h2>React Router Demo</h2>
          <button onClick={this.back}>回退</button>
          <button onClick={this.forward}>前进</button>
          <button onClick={this.goByNum}>前进或后退几部</button>
        </div>
    )
  }
}

// 暴露的是withRouter加工后的Header
export default withRouter(Header)

33 BrowserRouter和HashRouter的区别

1.底层原理不同
BrowserRouter使用的H5的history API,不兼容IE9及以下
HashRouter使用的是URL的哈希值
2.url的表现形式不同
BrowserRouter:没#
HashRouter:有#
3.刷新后对路由state参数的影响
BrowserRouter:没有任何影响,因为state保存在location对象中
HashRouter:导致路由state丢失

备注:HashRouter可以用于解决一些路径错误相关问题。

34组件库 ant-design

(1)基本使用

ant-design官网

安装 npm install antd --save

尚硅谷-React_第10张图片
(2)优化antd样式
修改主题色

  1. 安装依赖的包

npm install --save @craco/craco
npm i craco-less

  1. 修改样式引入

尚硅谷-React_第11张图片

  1. 在根目录下创建 craco.config.js文件并修改配置
const CracoLessPlugin = require('craco-less');

module.exports = {
  plugins: [
    {
      plugin: CracoLessPlugin,
      options: {
        lessLoaderOptions: {
          lessOptions: {
            modifyVars: { '@primary-color': '#1DA57A' },
            javascriptEnabled: true,
          },
        },
      },
    },
  ],
};

4.修改package.json文件

 "scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    "eject": "react-scripts eject"
  },

5.运行

npm start

35 redux

35.0 redux概念

redux是什么?

  • 是一个专门用于 管理状态 的js库,不是react插件库。
  • 它可以用在react,angular,vue中,但基本与react配合。
  • 作用:集中式管理react应用中多个组件 共享 的状态。

什么情况下用redux?

  • 某个组件的状态需要让其他的组件可以 随时 拿到(共享)。
  • 一个组件需要改变另一个组件的状态(通信)。
  • 总体使用原则:能不用就不用,如果不用的情况比较吃力才用。

尚硅谷-React_第12张图片

  • Action Creators:创建动作对象;
  • Store :管理者;
  • Reducers:初始化状态、加工状态;

35.1 redux的三个核心概念

1.action

动作对象
包含两个属性:

  • type:标识的属性,值为字符串,唯一,必要的属性
  • data:数据的属性,值类型任意,可选属性
    例:{type:‘ADD_STUDENT’,data:{name:‘tom’,age:18}}

2.reducer

1.用于初始化、加工状态
2.加工时,根据旧的state和action,产生新的state的纯函数

3.store

1.将state、action、reducer联系在一起的对象
2.如何得到此对象:
(1)import {createStore} from ‘redux’
(2)import reducer from ‘./reducers’
(3)const store=createStore(reducer)
3.此对象的功能:
(1)store.getState()得到state
(2)store.dispatch(action):分发action,触发reducer调用,产生新的state
(3)subscribe(listener)注册监听,当产生了新的state时自动调用

35.2 redux的精简使用

纯react版本

import React, { Component } from 'react'

export default class Count extends Component {
  state = {count:0}
  //加法
  increment=()=>{
    const {value} = this.selectNumber
    const {count} = this.state
    this.setState({count:count+value*1})
  }
  //减法
  decrement=()=>{
    const {value} = this.selectNumber
    const {count} = this.state
    this.setState({count:count-value*1})
  }
  //奇数加
  incrementOfOdd=()=>{
    const {value} = this.selectNumber
    const {count} = this.state
    if(count%2!==0){
      this.setState({count:count+value*1})
    }
  }
  //异步加
  incrementOfAsync=()=>{
    const {value} = this.selectNumber
    const {count} = this.state
    setTimeout(()=>{
      this.setState({count:count+value*1})
    },800)
  }
  render() {
    return (
      <div>
        <h1>当前求和为:{this.state.count}</h1>&nbsp;
        <select ref={c=>this.selectNumber=c}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>&nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementOfOdd}>当前求和为奇数加</button>&nbsp;
        <button onClick={this.incrementOfAsync}>异步加</button>
      </div>
    )
  }
}

redux版本

1.下载:npm i redux

store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
import { createStore } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './conut_reducer'


//暴露store
export default createStore(countReducer)

conut_reducer.js

//该文件是用于创建一个为count服务的reducer,reducer的本质就是一个函数

//reducer函数的两个参数
//preState:之前的状态
//action:要做的动作

//初始化状态
const initState = 0
export default function countReducer(preState=initState,action){    const {type,data} = action
    switch(type){
        case 'increment':
            return preState + data
        case 'decrement':
            return preState - data
        default:
            return preState
    }
}

注意:reducer中只写操作,判断条件等语句都写在组件中,reducers是纯函数。

count组件

import React, { Component } from "react";
//引入store用于获取store中保存的状态
import store from "../../redux/store";

export default class Count extends Component {
  increment = () => {
    //通知reducter加value
    const { value } = this.selectNumber;
    store.dispatch({type:'increment',data:value*1})
  };
  componentDidMount(){
      //检查redux中状态的变化,只要变化就调用render
      store.subscribe(()=>{
         this.setState({})
      })
  }
  render() {
    return (
      <div>
        {/* 获取store中的值 */}
        <h1>当前求和为{store.getState()}</h1>
        <select name="" id="" ref={c => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
      </div>
    );
  }
}

使用的三个注意点:

  • store.getState():获取store状态值。
  • store.dispatch({type:‘increment’,data:value*1}):告诉reducer动作对象
  • componentDidMount(){
    store.subscribe(()=>{
    this.setState({})
    })
    }:检查redux中状态的变化,只要变化就调用render

35.3 redux文件完整版

和精简版的区别在于
1.使用Action creator,不自传action
2.使用constant来规范类型

constant.js

// 该模块是用于定义action对象中的type类型 防止在编写过程中出错

export const INCREMENT = 'increment'

export const DECREMENT = 'decrement'

count_action.js

// 该文件专为Count组件生成action

import { INCREMENT,DECREMENT } from "./constant"

export const createIncrementAction = data => {
  return { type: INCREMENT, data };
};

export const createDecrementAction = data => {
  return { type: DECREMENT, data };
};

conut_reducer.js

//该文件是用于创建一个为count服务的reducer,reducer的本质就是一个函数
import { INCREMENT,DECREMENT } from "./constant"

//reducer函数的两个参数
//preState:之前的状态
//action:要做的动作
export default function countReducer(preState,action){
    console.log(preState,action)
    //初始化状态
    if(preState === undefined) preState = 0
    const {type,data} = action
    switch(type){
        case INCREMENT:
            return preState + data
        case DECREMENT:
            return preState - data
        default:
            return preState
    }
}

store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
import { createStore } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './conut_reducer'


//暴露store
export default createStore(countReducer)


Count组件

import React, { Component } from "react";
//引入store用于获取store中保存的状态
import store from "../../redux/store";
//引入actionCreate 专门用于创建action对象
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

export default class Count extends Component {
  increment = () => {
    //通知reducter加value
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value*1))
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAction(value*1))
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    const count = store.getState()
    if (count % 2 !== 0) {
        store.dispatch(createIncrementAction(value*1))
    }
  };
  componentDidMount(){
      //检查redux中状态的变化,只要变化就调用render
      store.subscribe(()=>{
         this.setState({})
      })
  }
  render() {
    return (
      <div>
        <h1>当前求和为{store.getState()}</h1>
        <select name="" id="" ref={c => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button>&nbsp;
        <button onClick={this.decrement}>-</button>&nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>&nbsp;
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    );
  }
}

35.4 异步action(复习的时候看这版)

action类型 说明
对象类型 同步action
函数类型 异步action

下载依赖:npm i redux-thunk (异步action传递到store的中间件,使store可以接收到非对象类型的action)

异步action就是指action的值为函数,异步action中一般会调用同步action,异步action不是必须要使用的

store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
import { createStore,applyMiddleware } from "redux";
// 引入为Count组件服务的reducer
import countReducer from './conut_reducer'
//引入 redux-thunk用于支持异步action
import thunk from "redux-thunk";


//暴露store
export default createStore(countReducer,applyMiddleware(thunk))


count_action.js

//同步action
export const createIncrementAction = data => {
  return { type: INCREMENT, data };
};
//异步action
export const createDecrementAsyncAction = (data,time) => {
    return (dispatch)=>{
        setTimeout(() => {
            dispatch(createIncrementAction(data))
        }, time);
    }
  };

异步action提供dispatch方法

Count组件

import React, { Component } from "react";
//引入store用于获取store中保存的状态
import store from "../../redux/store";
//引入actionCreate 专门用于创建action对象
import { createDecrementAsyncAction,createDecrementAction } from "../../redux/count_action";

export default class Count extends Component {
  incrementAsync = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAsyncAction(value*1,2000))
  };
  componentDidMount(){
      //检查redux中状态的变化,只要变化就调用render
      store.subscribe(()=>{
         this.setState({})
      })
  }
  render() {
    return (
      <div>
        <h1>当前求和为{store.getState()}</h1>
        <select name="" id="" ref={c => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <button onClick={this.incrementAsync}>异步加</button>&nbsp;
      </div>
    );
  }
}

36 react-redux

尚硅谷-React_第13张图片

安装:npm i react-redux

36.1 react-dedux的基本使用

  1. 将Count改名为CountUI,且只保存html结构
  2. 创建容器组件 在src下新建文件containers/Count/index用于放置CountUI的容器组件
//自定义容器组件

// 引入count的UI组件
import CountUI from '../../components/CountUI'
//引入connect用于链接容器组件和UI组件
import { connect } from "react-redux";


// 生成容器组件 并链接UI组件
const countContainer = connect()(CountUI)

export default countContainer

3.修改App.js中的引入,并向容器组件中传递store(到这一步就实现了容器和UI组件、容器和redux之间的连接)

import React, { Component } from 'react'
//渲染的是容器组件
import Count from './containers/Count'
import store from './redux/store'

export default class App extends Component {
  render() {
    return (
      <div><Count store={store}></Count></div>
    )
  }
}

4.Count容器向CountUI传props参数
尚硅谷-React_第14张图片
请添加图片描述

当容器组件与store连接后,UI组件的props会自动收到store

5.容器组件和redux的数据沟通:

import CountUI from '../../components/CountUI'
import { connect } from "react-redux";
import {createIncrementAction} from '../../redux/count_action'

//这个函数有一个参数为redux的state
function a(state){
    return {count:state}
}
//这个函数有一个参数为dispatch 用于操作store的值
function b(dispatch){
    return {add:(data)=>{
        //通知redux执行increase
        dispatch(createIncrementAction(value))
    }}
}
const countContainer = connect(a,b)(CountUI)

export default countContainer

规范命名:

  • a函数:mapStateToProps
  • b函数:mapDispatchToProps
import CountUI from '../../components/CountUI'
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

function mapStateToProps(state){
    return {count:state}
}
function mapDispatchToProps(dispatch){
    return {
        increace:data=>dispatch(createIncrementAction(data)),
        decreace:data=>dispatch(createDecrementAction(data))
    }
}
const countContainer = connect(mapStateToProps,mapDispatchToProps)(CountUI)

export default countContainer

6.UI组件使用props

increment = () => {
    const { value } = this.selectNumber;
    this.props.add(1*value)
 };
<h1>当前求和为:{this.props.count}h1>

36.2 react-dedux的优化

1.容器的优化
import CountUI from '../../components/CountUI'
import { connect } from "react-redux";
import { createIncrementAction,createDecrementAction } from "../../redux/count_action";

//简写方式
const countContainer = connect(
    //传递state参数
    state=>{return {count:state}},
    //对象的形式 只需提供action 就可以自动调用dispatch
    {
        increace:createIncrementAction,
        decreace:createDecrementAction
    }
)
(CountUI)

export default countContainer
2.无需使用store.subscribe进行监测

react-redux自动检测并更新,容器组件默认拥有检测redux变化的能力

3.App.js中传store的优化

优化:不在App.js中传,在index.js中传

App.js

import React, { Component } from 'react'
//渲染的是容器组件
import Count from './containers/Count'

export default class App extends Component {
  render() {
    return (
      // 给容器组件传递store
      <div><Count></Count></div>
    )
  }
}

index.js

import React from 'react';
import App from './App';
import { createRoot } from 'react-dom/client';
//引入store相关
import store from './redux/store';
import {Provider} from 'react-redux'


const container = document.getElementById('root');
const root = createRoot(container);
root.render( 
<Provider store={store}>
    <App />
</Provider>
);
4.整合容器组件和UI组件为一个组件
import React, { Component } from 'react'
import { connect } from "react-redux";
import {createIncrementAction,createDecrementAction,createIncrementActionAsync} from '../../redux/count_action'

//UI组件
class Count extends Component {
    state = {count:0}
    //加法
    increment=()=>{
      const { value } = this.selectNumber;
      this.props.increace(value*1)
    }
    //减法
    decrement=()=>{
      const {value} = this.selectNumber
      this.props.decreace(value*1)
    }
    //奇数加
    incrementOfOdd=()=>{
      const {value} = this.selectNumber
      if(this.props.count%2!==0){
        this.props.increace(value*1)
      }
    }
    //异步加
    incrementOfAsync=()=>{
      const {value} = this.selectNumber
      this.props.increaceAsyn(value*1,1000)
    }
    render() {
      console.log(this.props)
      return (
        <div>
          <h1>当前求和为:{this.props.count}</h1>&nbsp;
          <select ref={c=>this.selectNumber=c}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
          </select>&nbsp;
          <button onClick={this.increment}>+</button>&nbsp;
          <button onClick={this.decrement}>-</button>&nbsp;
          <button onClick={this.incrementOfOdd}>当前求和为奇数加</button>&nbsp;
          <button onClick={this.incrementOfAsync}>异步加</button>
        </div>
      )
    }
  }
  

//容器组件  
const countContainer = connect(
    //传递state状态
    state=>{return {count:state}},
    //传递方法 react-redux自动分发
    {
        increace:createIncrementAction,
        decreace:createDecrementAction,
        increaceAsyn:createIncrementActionAsync
    }
    
)(Count)

export default countContainer

37 redux数据共享

37.1 重新修改redux文件结构

尚硅谷-React_第15张图片

  • 文件也可以直接用count.js命名、不一定要用count_action.js/count_reducer.js
  • 文件内容按照redux需要来写 可以参考 “redux文件完整版”

37.2 store.js

多个组件共同使用redux的情况

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

// 引入createStore 用于创建store
// applyMiddleware:使异步action可以被接收
// combineReducers:合并reducer
import { createStore,applyMiddleware,combineReducers } from "redux";

// 引入为Count组件服务的reducer
import countReducer from "./reducers/count_reducer";
//引入person的reducer
import personReducer from "./reducers/person_reducer";

//引入 redux-thunk用于支持异步action
import thunk from "redux-thunk";

//汇总reducer
const allReducer= combineReducers({
    count:countReducer,
    person:personReducer
})
//暴露store
export default createStore(allReducer,applyMiddleware(thunk))

37.3 constant.js

// 用于定义action对象中type的常量值 防止写错
//count
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
//person
export const ADDPERSON = 'addperson'

37.4 action.js

person_action.js

import { ADDPERSON } from "../constant";

export const createAddPersonAction=(data)=>{
    return {type:ADDPERSON,data}
}

count_action.js

import { INCREMENT,DECREMENT } from "../constant";


export const createIncrementAction = data => {
    return { type: INCREMENT, data };
};

export const createDecrementAction = data => {
    return { type: DECREMENT, data };
};

export const createIncrementActionAsync = (data,time)=>{
    return (dispatch)=>{
        setTimeout(()=>{
            dispatch(createIncrementAction(data))
        },time)
    }
}
 

37.5 reducer.js

person_reducer.js

import { ADDPERSON } from "../constant";

const initState=[{id:'001',name:'tome',age:15}]
export default function personReducer(preState=initState,action){
    const {type,data} = action
    switch(type){
        case ADDPERSON:
            // return preState.push(data)
            return [data,...preState]
        default:  
            return preState
    }
}

count_reducer.js

// 初始化
import { INCREMENT,DECREMENT } from "../constant";
const initState = 0
export default function countReducer(preState = initState,action){
    const {type,data} = action
    switch(type){
        case INCREMENT:
            return preState = preState+data
        case DECREMENT:
            return preState = preState-data
        default:
            return preState
    }
}

37.3 组件内使用

person

import React, { Component } from 'react'
import {nanoid} from 'nanoid'
import { connect } from 'react-redux'
import { createAddPersonAction } from '../../redux/actions/person_action'

class Person extends Component {
  addPerson =()=>{
    const age  = this.age.value
    const name = this.name.value
    const person = {id:nanoid(), name,age}
    this.props.createAddPersonAction(person)
  }
  render() {
    return (
      <div>
        <h1>我是person组件</h1>
        <div>我拿到的count值:{this.props.count}</div>
        <input ref={c=>this.name=c} type="text" placeholder='输入名字'/>
        <input ref={c=>this.age=c} type="text" placeholder='输入年龄'/>
        <button onClick={this.addPerson}>添加</button>
        <ul>
          {this.props.person.map((item)=>{
            return <li key={item.id}>{item.name}--{item.age}</li>
          })}
        </ul>
      </div>
    )
  }
}


const PersonContainer = connect(
  state=>{return {
    count:state.count,
    person:state.person
  }},
  {
    createAddPersonAction:createAddPersonAction
  }
)(Person)

export default PersonContainer

count

import React, { Component } from 'react'
import { connect } from "react-redux";
import {createIncrementAction,createDecrementAction,createIncrementActionAsync} from '../../redux/actions/count_action'

//UI组件
class Count extends Component {
    increment=()=>{
      const { value } = this.selectNumber;
      this.props.increace(value*1)
    }
    //减法
    decrement=()=>{
      const {value} = this.selectNumber
      this.props.decreace(value*1)
    }
    //奇数加
    incrementOfOdd=()=>{
      const {value} = this.selectNumber
      if(this.props.count%2!==0){
        this.props.increace(value*1)
      }
    }
    //异步加
    incrementOfAsync=()=>{
      const {value} = this.selectNumber
      this.props.increaceAsyn(value*1,1000)
    }
    render() {
      return (
        <div>
          <h1>我是Coun组件</h1>
          <div>我拿到的person:</div>
          <ul>
            {this.props.person.map((item)=>{
              return <li key={item.id}>{item.name}-{item.age}</li>
            })}
          </ul>
          <h3>当前求和为:{this.props.count}</h3>&nbsp;
          <select ref={c=>this.selectNumber=c}>
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
          </select>&nbsp;
          <button onClick={this.increment}>+</button>&nbsp;
          <button onClick={this.decrement}>-</button>&nbsp;
          <button onClick={this.incrementOfOdd}>当前求和为奇数加</button>&nbsp;
          <button onClick={this.incrementOfAsync}>异步加</button>
        </div>
      )
    }
  }
  

//容器组件  
const countContainer = connect(
    //传递state参数
    state=>{return {count:state.count,person:state.person}},
    //传递方法 react-redux自动分发
    {
        increace:createIncrementAction,
        decreace:createDecrementAction,
        increaceAsyn:createIncrementActionAsync
    }
    
)(Count)

export default countContainer

app.js

import React, { Component } from 'react'
//渲染的是容器组件
import Count from './containers/Count/Count'
// import Test from './containers/test'
import Person from './containers/Person/Person'

export default class App extends Component {
  render() {
    return (
      // 给容器组件传递store
      <div>
        <Count></Count>
        <hr></hr>
        <Person></Person>
      </div>
    )
  }
}

最终效果
尚硅谷-React_第16张图片

38 纯函数

是一类特别的函数:只要是同样的输入(实参),必定得到同样的输出(返回)。
————————————————————————————
必须遵循以下规则:
(1)不得改写参数数据
(2)不会产任何副作用,例如网络请求,输入和输出设备
(3)不能调用Date.now()或者Math.randing()等不纯的方法
————————————————————————————
redux的reducer必须是一个纯函数

39 redux扩展插件应用

安装库:npm install redux-devtools-extension
修改redux/store.js

// 该文件专门用于暴露一个store对象,整个应用只有一个store对象

//引入redux-devtools-extension
import {composeWithDevTools} from 'redux-devtools-extension'

// 引入createStore 用于创建store
// applyMiddleware:使异步action可以被接收
// combineReducers:合并reducer
import { createStore,applyMiddleware,combineReducers } from "redux";

// 引入为Count组件服务的reducer
import countReducer from './reducers/conut'
//引入person的reducer
import personReducer from "./reducers/person";

//引入 redux-thunk用于支持异步action
import thunk from "redux-thunk";

//汇总reducer
const allReducer= combineReducers({
    count:countReducer,
    person:personReducer
})
//暴露store
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)))

40 打包react项目

1.执行:npm run build
生成build文件

2.安装库:npm i serve -g
用于开启服务器

3. 执行:serve build
以build文件夹作为根路径来启动一台服务器

在工作上是用不到的

扩展1-关于setState()

(1). setState(stateChange, [callback])------对象式的setState
1.stateChange为状态改变对象(该对象可以体现出状态的更改)
2.callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用
setState引起react更新的动作是异步的

(2). setState(updater, [callback])------函数式的setState
1.updater为返回stateChange对象的函数。
2.updater可以接收到state和props。
4.callback是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

总结:
1.对象式的setState是函数式的setState的简写方式(语法糖)
2.使用原则:
(1).如果新状态不依赖于原状态 ===> 使用对象方式
(2).如果新状态依赖于原状态 ===> 使用函数方式
(3).如果需要在setState()执行后获取最新的状态数据,
要在第二个callback函数中读取

1.setState()的第一种用法

  //setState的第一种用法
  add = () => {
      const {count}=this.state
      this.setState({count:count+1},()=>{
          console.log("在回调函数中",this.state)
      })
      console.log("不在回调函数中",this.state)
  };

输出结果:
不在回调函数中 {count: 0}
在回调函数中 {count: 1}

2.setState()的第二种用法

  add=()=>{
      this.setState((state,props)=>{
          return {count:state.count+1}
      })
  }

扩展2-LazyLoad懒加载

通过React的lazy函数配合import()函数动态加载路由组件 ===> 路由组件代码会被分开打包
const Login = lazy(()=>import('@/pages/Login'))

通过React的Suspense来控制组件还没有加载好时的状态
loading...

}

// 引入lazy,Suspense
import React, { Component,lazy,Suspense } from "react";
import {NavLink,Route } from "react-router-dom";
//lazy和import一起使用 注册可以懒加载的组件
const Home= lazy(()=>import('./Home'))
const About= lazy(()=>import('./About'))

//创建并暴露App外壳组件s
export default class App extends Component {
  render() {
    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">
              {/* 在react考路由链接切换组件 */}
                <NavLink className="list-group-item" to="/about" >about</NavLink>&nbsp;
                <NavLink className="list-group-item" to="/home">home</NavLink>
            </div>
          </div>
          <div className="col-xs-6">
            <div className="panel">
              <div className="panel-body">
                  {/* 注册路由 */}
                  {/* 使用Suspense来控制组件还没有加载好时的状态,这里最好是一个loading的组件 */}
                    <Suspense fallback={<h1>loading...</h1>}>
                        <Route path="/about" component={About}/>
                        <Route path="/home" component={Home}/>
                    </Suspense>
              </div>
            </div>
          </div>
        </div>
    </div>
    )
  }
}

扩展3-Hooks

什么是Hooks?
(1). Hook是React 16.8.0版本增加的新特性/新语法
(2). 可以让你在函数组件中使用 state 以及其他的 React 特性

3个常用的hooks
(1). State Hook: React.useState()
(2). Effect Hook: React.useEffect()
(3). Ref Hook: React.useRef()

React.useState():使函数式组件能有state和修改state

export default function Demo() {
  //React.useState的参数为state的初始值
  //React.useState的返回值为一个数组 第一个值为state的值 第二个值为改变state的方法
  const [count,setCount] = React.useState(0);

  function add() {
    //setState的第一种写法
    setCount(count+1)
    //setState的第二种写法
    setCount(count=>count+1)
  }
  return (
    <div>
      <h1>现在的值为:{count}</h1>
      <button onClick={add}>点我加1</button>
    </div>
  );
}

注意:在更新对象类型时,切记要合并旧的状态,否则旧的状态会丢失。

const [params, setParams] = useState({
  rotate: 0,
  color: "#000000"
});

const handleInputChange = event => {
  const target = event.target;
  setParams({
    ...params,
    [target.name]: target.value
  });
};

注意:因为是要修改useState处理后的值,所有需要使用useCallback来处理,其他要进行一次浅拷贝

 const emailMouseLeave = useCallback((changeIndex)=>{
    let result = email.concat()
    result.forEach((item,index)=>{
      if(index === changeIndex){
        item.isHover = false
      }
    })
    setEmail(result)
  },[email])

React.useEffect():使函数式组件具备钩子函数

export default function Demo() {
   const [count,setCount] = React.useState(0);
  //React.useEffect可以传入两个参数 
  //第一个参数 :为一个函数,这个函数就相当于一个生命周期的回调函数
  //第二个参数 :为一个数组,里面的值相当于监测谁 
  //          不写:谁都监测,
  //          空数组:谁都不监测,此时第一个参数相当于componentDidMount的回调
  //          [count,name]:监测count和name,此时第一个参数相当于componentDidUpdate的回调

  //React.useEffect 返回值为一个函数,这个函数就相当于componentWillUnmount的回调
  React.useEffect(() => {
      let timer=setInterval(()=>{
          setCount(count=>count+1)
      },1000)
      return ()=>{
          clearInterval(timer)
      }
  },[]);
  //卸载组件的回调
  function unMount(){
    ReactDom.unmountComponentAtNode(document.getElementById('root'))
  }
  return (
    <div>
      <h1>现在的值为:{count}</h1>
      <button onClick={unMount}>卸载组件</button>
    </div>
  );
}

卸载组件:
引入:import ReactDom from ‘react-dom’
卸载:ReactDom.unmountComponentAtNode(document.getElementById(‘root’))

React.useRef():使函数组件支持ref

export default function Demo() {
   const myRef=React.useRef()
   function show(){
       alert(myRef.current.value)
   }
   return (
     <div>
       <input type="text" name="" id="" ref={myRef}/>
       <button onClick={show}>点击按钮展示内容</button>
     </div>
   );
 }

扩展4-Fragment

Fragment的作用?
代替div在解析的时候会忽略掉,就不会出现不必要的真实dom外层标签了

import React, { Component,Fragment } from 'react'
import Demo from './components/4_Fragment'

export default class App extends Component {
  render() {
    return (
        <Fragment>
          <Demo></Demo>
        </Fragment>
    )
  }
}

Fragment可以接收一个参数 key,可以用于循环遍历的时候

扩展5-Context

1.Context的作用?
用于组件间的通信,常用与【祖组件】和【后代组件】之间

使用context

1.创建context容器对象,并取出Provider

const UserNameContext=React.createContext()
const {Provider,Consumer} = UserNameContext

2.渲染子组件的时候。外面要包裹Provider,通过value属性给后代传递数据

export default class A extends Component {
  state={username:'tom'}
  render() {
    return (
      <div className='parent'>
          <h3>我是A组件</h3>
          <h4>我的用户名是{this.state.username}</h4>
          //必须是value 不允许改名
          <Provider value={this.state.username}>
            <B/>
          </Provider>
      </div>
    )
  }
}

3.后代读取数据
(1)方法一:仅用于类式组件

class C extends Component {
  //声明接收
   static contextType=UserNameContext
    render() {
      console.log(this)
      return (
        <div className='grand'>
          <h3>我是B组件</h3>
          <h4>我从A组件接收到的用户名是:{this.context}</h4>
        </div>
      )
    }
}

(2)方法二:函数式组件和类式组件都可以使用

function C(){
  return (
    <div className='grand'>
      <h3>我是B组件</h3>
      <h4>我从A组件接收到的用户名是:
        <Consumer>
          {
            value=>{
              return `${value.username}`
            }
          }
        </Consumer>
      </h4>
    </div>
  )
}

在应用的开发中一般不使用context,一般用它来封装react插件

扩展6-PureComponent

component存在的两个问题:
(1)只要执行了setState即使不改变数据组件也会重新render。
(2)当组件重新render(),就会自动重新render子组件,即使子组件没有用父组件的任何东西的时候。
----》效率低

原因:
component中的shouldComponentUpdate()总是返回true

解决1:重写shouldComponentUpdate()

  shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps, nextState); //要变化成的目标props,state
    console.log(this.props, this.state); //变化前的props,state
    if (this.state.carName === nextState.carName) return false;
  }

解决2:不用component而用purcomponent【内部实现就是重写了shouldComponentUpdate】
使用这种方法不要直接修改数据而要产生新的数据
尚硅谷-React_第17张图片

import React, { PureComponent } from "react";
import "./index.css";

export default class Parent extends PureComponent {
  state = { carName: "car1" };
  changeCar = () => {
    this.setState({ carName: "car2" });
  };
  shouldComponentUpdate(nextProps, nextState) {
    console.log(nextProps, nextState); //要变化成的目标props,state
    console.log(this.props, this.state); //变化前的props,state
    if (this.state.carName === nextState.carName) return false;
  }
  render() {
    return (
      <div className="parent">
        <h2>我是Parent</h2>
        <button onClick={this.changeCar}>点我换车</button>
        <h4>我的车是:{this.state.carName}</h4>
        <Child carName={this.state.carName}/>
      </div>
    );
  }
}

class Child extends PureComponent {
//   shouldComponentUpdate(nextProps, nextState) {
//     console.log(nextProps, nextState); //要变化成的目标props,state
//     console.log(this.props, this.state); //变化前的props,state
//     if (this.props.carName === nextProps.carName) return false;
//   }
  render() {
    return (
      <div className="child">
        <h2>我是Child</h2>
        <h4>我接到的车是:{this.props.carName}</h4>
      </div>
    );
  }
}

扩展7-renderProps

import React, { Component } from "react";
import "./index.css";

export default class Parent extends Component {
  state = { username: "tom" };
  render() {
    return (
      <div className="parent">
        <h3>我是Parent组件</h3>
        <h4>我的用户名是{this.state.username}</h4>
        //传参
        <A render={(name) => <B name={name}/>} />
      </div>
    );
  }
}

class A extends Component {
  state = { name: "tom" };
  render() {
    const {name} = this.state
    console.log(this.props);
    return (
      <div className="child">
        <h3>我是A组件</h3>
        //调用子组件
        {this.props.render(name)}
      </div>
    );
  }
}

//类式组件接收
class B extends Component {
  render() {
    return (
      <div className="grand">
        <h3>我是B组件</h3>
        //接收
        <h4>我接到的名字:{this.props.name}</h4>
      </div>
    );
  }
}

扩展8:-ErrorBoundary错误边界

用于捕获组件生命周期产生的错误,一般是当后端返回的数据不正确的时候使用
错误边界:不要让一个组件的错误影响整个组件,使错误控制在一定的范围内

import React, { Component } from "react";
import Child from "./Child";

export default class Prrent extends Component {
  //hasError:用于标识子组件是否产生错误
  state={hasError:''}
  //当parent的子组件出现错误的时候会调用getDerivedStateFromError并且携带错误信息
  static getDerivedStateFromError(error){
    console.log(error)
    return {hasError:error}
  }

  //渲染组件出错会调用这个函数,一般用于做统计错误,反馈给服务器,用于通知编码人员进行bug修复
  componentDidCatch(){
    console.log('渲染组件出错')
  }
  
  render() {
    return (
      <div>
        <h2>我是Parent组件</h2>
        {this.state.hasError?<h2>出错啦...</h2>:<Child />}
      </div>
    );
  }
}

错误边界只使用与生产环境,在开发环境下还是会报错。

扩展9-组件之间通信方式的总结

1.组件见的关系

  • 父子
  • 兄弟
  • 子孙

2.组件之间的通信方式

  • props: (1)chilren props (2)render props

  • 消息订阅-发布

  • 集中式管理 redux

  • conText 生产者-消费者模式

3.比较好的搭配

  • 父子组件:props
  • 兄弟组件:消息订阅预发布、集中式管理
  • 祖孙组件:消息订阅与发布、集中式管理、conText(开发用的少,封装插件用的多)

41 react-router6

安装:npm i react-router-dom

(41-1)router6的基本使用

默认使用(不区分大小写):
区分大小写使用:caseSensitive path=“/about” element={} />

import React from "react";
import {NavLink,Routes,Route } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
export default function App() {
  return (
    <div>
      <div className="row">
        <div className="col-xs-offset-2 col-xs-8">
          <div className="page-header">
            <h2>React Router Demoh2>
          div>
        div>
      div>
      <div className="row">
        <div className="col-xs-2 col-xs-offset-2">
          <div className="list-group">
            {/* 在react考路由链接切换组件 */}
            <NavLink className="list-group-item" to="/about"> aboutNavLink>
            <NavLink className="list-group-item" to="/home">HomeNavLink>
          div>
        div>
        <div className="col-xs-6">
          <div className="panel">
            <div className="panel-body">
              {/* 注册路由 */}
              <Routes>
                  <Route path="/about" element={>} />
                  <Route path="/home" element={>} />
              Routes>
            div>
          div>
        div>
      div>
    div>
  );
}

对比route5:
(1)用Routes代替Switch包裹Route
(2)用 } /> 代替

(41-2)router6的重定向

基本使用(默认push形式跳转):
replace形式跳转 :replace={true}/>

1.注册路由时使用

{/* 注册路由 */}
<Routes>
    <Route path="/about" element={>} />
    <Route path="/home" element={>} />
    } />
Routes>

2.路由跳转时使用

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

export default function Home() {
  const [state,setState] = useState({num:1})
  function addStateNum(){
    setState({num:state.num+1})
  }
  return (
    <div>
      <h2>Home组件的内容</h2>
      //值为2的时候跳转到about组件
      {state.num === 2?<Navigate to='/about'/>:<h4>当前sum的值为:{state.num}</h4>}
      <button onClick={addStateNum}>点我+1</button>
    </div>
  )
}

(41-3)router6的NavLink高亮效果

基本使用:

<NavLink className={({isActive})=>isActive?'weijiamin':'list-group-item'} to="/about"> about</NavLink>

优化:

  function computedClassName({isActive}){
    return isActive?'list-group-item weijiamin':'list-group-item'
  }
{/* 在react考路由链接切换组件 */}
<NavLink className={computedClassName} to="/about"> aboutNavLink>
<NavLink className={computedClassName} to="/home">HomeNavLink>

(41-4)router6的路由表的使用

import {useRoutes} from "react-router-dom";
路由表
  const elements=useRoutes([
    {path:'/about',element:<About/>},
    {path:'/home',element:<Home/>},
    {path:'/',element:<Navigate to={'/about'}/>},
  ])
    <div>
      <div className="row">
        <div className="col-xs-offset-2 col-xs-8">
          <div className="page-header">
            <h2>React Router Demoh2>
          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"> aboutNavLink>
            <NavLink className="list-group-item" to="/home">HomeNavLink>
          div>
        div>
        <div className="col-xs-6">
          <div className="panel">
            <div className="panel-body">
              {/* 注册路由 */}
              {elements}
            div>
          div>
        div>
      div>
    div>

一般是把路由表的数据放在文件 src/routers/index.js 中,再在组件中引入

import Home from "../pages/Home";
import About from "../pages/About";
import { Navigate } from "react-router-dom";

const routes = [
  { path: "/about", element: <About /> },
  { path: "/home", element: <Home /> },
  { path: "/", element: <Navigate to={"/about"} /> },
];

export default routes;
import React from "react";
import {NavLink,useRoutes} from "react-router-dom";
import routes from "./routes";
export default function App() {
  const elements=useRoutes(routes)
  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" to="/home">Home</NavLink>
          </div>
        </div>
        <div className="col-xs-6">
          <div className="panel">
            <div className="panel-body">
              {/* 注册路由 */}
              {/* 
                  } />
                  } />
                  } />
               */}
              {elements}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

(41-5)router6嵌套路由

注册路由表
  {
    path: "/home",
    element: <Home />,
    children: [
      { path: "news", element: <News /> },
      { path: "message", element: <Message /> },
    ],
  },
import React from 'react'
import {NavLink,Outlet} from 'react-router-dom'
    <div>
      <h2>Home组件的内容h2>
      <div>
          <div className="list-group">
            <NavLink className="list-group-item" to="news">aboutNavLink>
            <NavLink className="list-group-item" to="message">aboutNavLink>
          div>
          <div>
            {/* 指定路由组件呈现的位置 */}
            <Outlet/>
          div>
      div>
    div>

(41-6)router6路由传参

1.传递params参数:

声明传递params参数

<NavLink to={`detail/${item.id}/${item.title}/${item.content}`}>{item.title}NavLink>

声明接收params参数

{
  path: "message",
  element: <Message />,
  children: [
    {
      // 声明接收params参数
       path: "detail/:id/:title/:content",
       element: <Detail />,
    },
  ],
},

接收params参数
尚硅谷-React_第18张图片

1.传递search参数:

声明传递search参数:

<NavLink to={`detail?id=${item.id}&title=${item.title}&content=${item.content}`}>{item.title}</NavLink>

声明接收search参数:

{
  path: "message",
  element: <Message />,
  children: [
    {
      // 声明接收params参数
       path: "detail",
       element: <Detail />,
    },
  ],
},

接收search参数:
尚硅谷-React_第19张图片

setSearch的用法:

import React from "react";
import { useSearchParams } from "react-router-dom";

export default function Detail() {
  const [search,setSearch] = useSearchParams()
  return (
    <div>
      <ul>
        <li>
          <button onClick={()=>{setSearch('id=11&title=哈哈&content=呜呜')}}>点我更新收到的serch参数</button>
        </li>
        {/* 调用serrch身上的get方法来获值 */}
        <li>{search.get('id')}</li>
        <li>{search.get('title')}</li>
        <li>{search.get('content')}</li>
      </ul>
    </div>
  );
}

3.传递state参数

声明传递state参数:

<NavLink to="detail" state={{id:item.id,title:item.title,content:item.content}}>
    {item.title}
NavLink>

声明接收state参数:

{
  path: "message",
  element: <Message />,
  children: [
    {
      // 声明接收params参数
       path: "detail",
       element: <Detail />,
    },
  ],
},

接收state参数:
尚硅谷-React_第20张图片

(41-7)编程式路由导航

可以用于路由组件和非路由组件

import { NavLink,Outlet,useNavigate} from "react-router-dom";
  const navigate = useNavigate()
  function toDeatil(item){
    //第一个参数是要跳转的路径、第二个参数是一个配置对象
    navigate('detail',{
      replace:false,
      //目前只能携带state参数 如果要携带其他类型的参数,拼在路径中
      state:{
        id:item.id,
        title:item.title,
        content:item.content
      }
    })
  }
  function goForward(){
    navigate(1)
  }
  function goBack(){
    navigate(-1)
  }

(41-8)Hook-useInRouterContext

用于判断当前组件是否处于的上下文环境中,如果是,则返回true否则返回false

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

console.log(useInRouterContext())

(41-9)Hook-useNavigationType

作用:用于返回当前的导航类型(用户是如何来到当前页面的)
返回值:POP、PUSH、REPLACE
备注:POP是指在浏览器中直接打开了这个路由组件(刷新页面)

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

console.log(useNavigationType())

(41-10)Hook-useOutlet

作用:用于返回当前组件中渲染的嵌套路由
说明
如果嵌套的路由组件还没有挂载,则返回null
如果嵌套的路由组件已经挂载,则展示嵌套的路由对象

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

console.log(useOutlet())

(41-11)Hook-useResolvePath()

作用:给一个url值,解析其中的:path,search,hash值

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

console.log(useResolvePath('/user?id=88&name=777'))

输出内容
{hash: “”
pathname: “/user”
search: “?id=88&name=777”}

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