DaZeng:React的基础使用及开发

21 篇文章 1 订阅
3 篇文章 0 订阅

React官网

官网地址

安装create-react-app脚组架

优点:无需配置webpack,babel(把es6,7,8,9,…转换为es5的工具)

卸载/安装

npm uninstall -g create-react-app
npm install -g create-react-app

查看版本号

npx create-react-app --version
在这里插入图片描述
npx:npm 5.0+提供的一个命令;它可以执行安装依赖包,npx本身不需要单独安装

创建项目并启动

create-react-app xxx
或:npx create-react-app xxx
主要安装了react, react-dom, and react-scripts

主要安装: react.js, react-dom.js, react-scripts.js

cd xxx
npm start
打包npm run bulid

项目依赖与开发依赖

项目依赖:npm i --save xxx
开发依赖:npm i --save-dev xxx; npm i -D xxx

开发依赖在项目打包时不会打包到项目中。

项目目录

public/     静态资源的目录。
    -index.html
    -favicon.ico  网站小图标
    -logo192.png ,打包成APP的小图标。
    -manifest.json  打包成APP的配置。
    -robots.txt  谷歌搜索引擎用的一个配置文件。
src/        开发的源代码存放的位置。 里面的所有文件,webpack都会进行打包处理。
   index.js  项目的入口文件

了解ES6中的类的语法

 // 1。类名,首字母要大写
        class Person {
            // 构造器中的属性,会做为实例化对象的直接属性
            constructor(name, age) {
                this.name = name;
                this.age = age;
            }
            // 类中的方法,会在实例化对象的原型对象上显示;
            // say
            say(text) {
                console.log(this.name + "说:" + text);
            }

            // write()
            //不要要类中,写方法时,加function这个关键字。
            write(text) {
                console.log(this.name + "写:" + text);
            }
        }
    // 2 子类继承父类
        class Student extends Person {
            constructor(name, age, className) {
                // 只要使用了extends关键字,就必须要写super()
                super(name, age); //通过super(参数列表)方法,去实现继承父类的属性;   
                this.className = className;
            }
            study() {

            }
        }

     //实例化对象:
      let s = new Student("张三", 18, "一班")
      console.log(s);   

map/filter/every/find/some

JS中的Array.prototype.map(callback):创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

filter() 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素。

every() 方法测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。

find() 方法返回数组中满足提供的测试函数的第一个元素的值。否则返回 undefined。

some() 方法测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值

JSX的基本使用

创建文件

创建一个login页:
在这里插入图片描述
index.js中引入
在这里插入图片描述

Fragment

在这里插入图片描述
审查元素多一层div:
在这里插入图片描述
使用Fragment:
在这里插入图片描述
在这里插入图片描述
或者使用空标签<></>:
在这里插入图片描述
在这里插入图片描述

变量、表达式

{this.state.xxx}

import React, { Component,Fragment } from 'react'

export default class About extends Component {
    render() {
        let name = 'About'
        let user = {
            name:'zz',
            age:22
        }
        let getName = function () {
            return 'll'
        }
        let list = ['a','b','c','d']
        let ahref = 'http://www.baidu.com'
        return (
            <Fragment>
                <h1>{name}</h1>
                <p>{user.name}</p>
                <p>{getName()}</p>
                <p>{list.join('$$')}</p>
                <p>{user.age<19?'未成年':'成年'}</p>
                <a href={ahref}>超链接</a>
            </Fragment>
        )
    }
}

图片的四种处理:

//静态加载图片
import log from "./assets/1.jpg";
 <img src={log} />

//动态加载图片,本地src目录下的图片,不能直接使用字符串来表示源。
<img src={require("./assets/1.jpg").default} />

//使用的是public目录中的图片。
 <img src="logo192.png" />
 
//网络资源 :
 <img src="https://xxx/react.png" />

class style

class属性,要写成className。不然就是写成了ES6的class关键字。

render() {
        const styledata = {color:'yellow'}
        return (
            <div>
                <p className='pstyle' style={styledata}>hahah</p>
                 <p className='this.state.className1' style={{backgroundColor:'red'}}>hahah</p>
            </div>
        )
    }

原生的html

__html:“html代码”
dangerouslySetInnerHTML={doHtmlData} 引入

render() {
        const htmlData = "<a href='#'>html a</a>"
        const doHtmlData = {
            __html:htmlData
        }
        return (
            <div>
                <p dangerouslySetInnerHTML={doHtmlData}></p> //渲染hml
                <p>{htmlData}</p> //无法渲染
            </div>  
        )
    }

在这里插入图片描述

htmlFor

 <label for="">
  <input id=“”/>
 </label>

 //写成:
 <label htmlFor="">
  <input id=“”/>
 </label>

条件判断

if else,三元运算符,&& 和 ||

render() {
        let user = {
            name:'zz',
            age:22
        }
        let str;
        if(user.name<18){
            str = <p>未成年</p>
        }else{
            str = <p>成年</p>
        }
        return (
            <Fragment>
                {str}
            </Fragment>
        )
    }

列表渲染

map:按照约定的规则,返回一个新的数组

import React, { Component,Fragment } from 'react'
export default class About extends Component {
    render() {
        let list = ['a','b','c','d']
        let list2 = list.map((item,index)=>{
            return (
                <li key={index}>{item}</li>
            )
        })
        let list3 = ()=>{
            let newList = []
            let index = 0
            for(let key in list){
                newList.push(
                    <li key={index++}>{list[key]}</li>
                )
            }
            return newList
        }

        return (
            <Fragment>
                <ul>
                   {list.map((item,index)=>{
                      return <li key={index}>{item}</li>
                   })}
                </ul>
                
                <ul>
                    {list2}
                </ul>

                <ul>
                    {list3()}
                </ul>

            </Fragment>
        )
    }
}

react 组件

概念

组件主要是用来高耦合,低内聚。
高耦合:把逻辑紧密的内容,都放在一个组件 中;
低内聚:不同组件的依赖关系要尽量弱化,每个组件尽量的独立出来;

传统组件的特点:进行简单的封装,简单的生命周期的呈现,有数据的流动。缺点是不能把组件中的结构和样式以及行为结合在一起,维护困难。

组件的主要内容:构建方式,组件有哪些属性,生命周期的应用。

组件化:就是你的程序使用组件来开发并完成的,这就是组件化的应用。

主要包括: state, props, 生命周期;
特点: 分离页面中的UI部分。让每一个组件尽量的细小,让其它开发者可以共享组件 。

组件是用来实现页面局部功能的代码的集合。是用来简化页面的复杂程序的,所以不要在一个组件中写太多的内容。

分类

有状态的组件:class组件,它有state属性
无状态的组件:函数式组件,它没有state属性; 它利用HOOKS技术来处理状态。

事件以及事件参数event

bind is
关于event参数
传递自定义参数

方法一

使用bind修改this指向

constructor(props){
        super(props)
        this.state = {
            name:'曾曾'
        }
        //修改this的指向
        this.clickChange = this.clickChange.bind(this)
    }
    //普通函数
    clickChange(e){
       this.setState({
           name:'zzz'
       })
    }
    render() {
        return (
            <div>
   传递事件对象:<p onClick={(e)=>this.clickChange('123',100,e)}>
   不传事件对象:<p onClick={this.clickChange}>
                点我触发事件修改:{this.state.name}
                </p>
            </div>
        )
    }

方法二(不推荐)

下方写bind
方法二比较方法一缺点:每点击一次都会调用一次bind性能低,而方法一只调一次。

 <p onClick={this.clickChange.bind(this,参数)}>
    点我触发事件修改:{this.state.name}
 </p>

方法三(推荐)

使用箭头函数

 clickChange3 = (num,e) =>{
 	console.log(e) //SyntheticBaseEvent
     this.setState({
         name:'zzz'
     })
  }
-----------------------------------------------
传递事件对象: <p onClick={(e)=>this.clickChange3(99,e)}>
不传事件对象,默认函数接收第一个参数是e
 <p onClick={this.clickChange3}>
     点我触发事件修改:{this.state.name}
 </p>

SyntheticBaseEvent 组合event 是react封装的,原生的event是MouseEvent
event.nativeEvent可以获取原生的event
所有的事件都挂在document上 e.nativeEvent.currentTarget 和 e.currentTarget不同

表单

受控组件

input、textarea、select、checkbox、radio
input的值和state中的值进行绑定,input的值受state控制。
实现的效果类似于:v-model的双向数据绑定。

 constructor(props){
        super(props)
        this.state = {
            val:'',
            checked:true
        }
        this.changeVal = this.changeVal.bind(this)
    }
    changeVal(e){
        this.setState({
            val:e.target.value,
            checked:!this.state.checked
        })
    }
    render() {
        return (
            <div>
                <p>{this.state.val}</p>
                <div>
                <input value={this.state.val} onChange={this.changeVal}></input>
                </div>

                <textarea value={this.state.val} onChange={this.changeVal}></textarea>
                
                <div>
                    <select value={this.state.val} onChange={this.changeVal}>
                        <option value='beijin'>北京</option>
                        <option value='shanghai'>上海</option>
                    </select>
                </div>

                <div>
                    <input type='checkbox' checked={this.state.checked}></input>
                    <p>{this.state.checked.toString()}</p>
                </div>
                <input type='radio' checked={this.state.checked}></input>
            </div>
        )
    }

组件的使用

props传递数据、函数、prop-types做类型检查
在这里插入图片描述

父子传参事件案例

//父
import React, { Component,Fragment } from 'react';
import Son from './son';
export default class About2 extends Component {
    constructor(){
        super()
        this.state={
            name:'zz',
            pass:'123',
            redStyle:{
                backgroundColor:'black',
                color:'red'
            }
        }
    }
    changeName = (newName,e)=>{
        this.setState({
            name:newName
        })
        console.log(e);
    }
    render() {
        return (
            <Fragment>
                <p>姓名:{this.state.name}</p>
                <button onClick={(e)=>this.changeName('ddd',e)}>点击更改姓名</button>
                <hr />
                <Son 
                text={this.state.name} 
                style={this.state.redStyle}
                myClick={this.changeName}
                />
            </Fragment>
        )
    }
}

子props接收

//子props接收
import React, { Component,Fragment } from 'react'

export default class Son extends Component {
    render() {
        return (
            <Fragment>
                <button type='button' 
                style={this.props.style}
                onClick={(e)=>this.props.myClick('Son Click1',e)}
                >
                    Son btn111 {this.props.text}
                </button>

                <button type='button' 
                style={this.props.style}
                onClick={this.props.myClick.bind(this,'Son Click2')}
                >
                    Son btn222 {this.props.text}
                </button>
            </Fragment>
        )
    }
}

在这里插入图片描述
在这里插入图片描述

todoList案例

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

过程及PropTypes类型检查

propTypes类型检查详细文档
父组件存放 Input 和 List 两个子组件以及总数据
List:父组件传递todolist给它循环渲染
Input:父组件传递写入的方法给它进行传参

安装开发依赖
npm i -D prop-types

案例:

import PropTypes from 'prop-types';
组件名.propTypes = {
  props中的属性名: PropTypes.string
};
//如:
Button.propTypes = {
  myType: PropTypes.string,
  myClick: PropTypes.func,
  myStyle: PropTypes.object,
  title: PropTypes.string,
};

//defaultProps
//指定 props 的默认值:
Button.defaultProps = {
  name: 'name没有传时默认显示的值'
};

//或者使用static
class Greeting extends React.Component {
  static defaultProps = {
    name: 'stranger'
  }
  static propTypes = {
	  myType: PropTypes.string
   };
  render() {
    return (
      <div>Hello, {this.props.name}</div>
    )
  }
}

父组件:

import React, { Component } from 'react'

import Input from '../component/Input'
import List from '../component/List'

export default class PropsDemo extends Component {
    constructor(props){
        super(props)
        //数据提升
        this.state = {
            val:'',
            todolist:[
                {id:1,text:'aaaaa'},
                {id:2,text:'b'},
                {id:3,text:'cc'},
                {id:4,text:'dddd'}
            ]
        }
        this.onaddText = this.onaddText.bind(this)
    }
    render() {
        return (
            <div>
                {/* 父--》子函数 */}
                <Input addText = {this.onaddText}></Input>

                {/* //父->子渲染  //类型检查*/}
                <List list={this.state.todolist}></List>
            </div>
        )
    }
    onaddText(data){  
        this.setState({
            todolist:[...this.state.todolist,
                {
                    id:this.state.todolist.length+1,
                    text:data
                }
            ]
        })
    }
}

List组件:
const {list} = this.props //解构赋值再进行数据处理

import React, { Component } from 'react'

//类型检查
import PropTypes from 'prop-types'

export default class List extends Component {
    render() {
        const {list} = this.props
        console.log(list);
        return (
            <div>
                <ul>
                    {
                        list.map((item)=>{
                            return <li key = {item.id}>{item.text}</li>
                        })
                    }
                </ul>
            </div>
        )
    }
}

//props 类型检查
List.propTypes = {
     //list是一个数组,数组里面每一项是一个对象
     // 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
     // 这个 prop 没有被提供时,会打印警告信息。
    list:PropTypes.arrayOf(PropTypes.object).isRequired
}

Input 组件:
const {addText} = this.props //解构赋值
addText(this.state.data) //作为函数传参调用

import React, { Component } from 'react'

export default class Input extends Component {
    constructor(props){
        super(props)
        this.state={
            data:''
        }
    }
    render() {
        return (
            <div>
                <p>Data:{this.state.data}</p>
                <input value={this.state.data} onChange={this.changeData}></input>
                <button onClick={this.onSubmit}>添加</button>
            </div>
        )
    }
    changeData = (e) =>{
        this.setState({
            data:e.target.value
        })
    }
    onSubmit = () =>{
        //把父函数传过来给父函数传参
        const {addText} = this.props
        console.log(addText);
        addText(this.state.data)
        this.setState({
            data:''
        })
    }
}

fetch

在这里插入图片描述

login() {
    let userInfo = {
        user: this.state.uIn,
        pass: this.state.uPsIn
    }
    let that = this
    var url = 'http://127.0.0.1:8001/login';
    fetch(url,{
        method:'POST',
        body: JSON.stringify(userInfo),
        mode: 'cors',
        headers: {
            'Content-Type': 'application/json',
        }
    })
    .then((response)=>{
        console.log(response);
        if (response.status === 200) {
            return response.json()
        }
    })
    .then((res)=>{
        console.log(res);
        if (res.state == 1) {
            alert('登录成功')
        } else {
            alert('登录失败')
        }
        that.setState({
            uIn: '',
            uPsIn: ''
        })
    })
    .catch(error => console.error('Error:', error))
}

非受控组件 refs

转发refs,react中提供一个ref的数据;
refs表示当前组件的真正实例的引用 ,它会返回绑定的当前属性的元素。
当绑定ref后,就标识组件中内容的元素,方便我们查找属性。
早期只能在类组件中使用,函数式组件中不能使用。HOOKS可以解决函数式组件没有state的问题。

从性能上来讲,要减少refs的使用。
ref、defaultValue defaultChecked、手动操作DOM元素

方案一:

创建ref:this.inputVal = React.createRef()
调用ref: input ref={this.inputVal}
获取值:this.inputVal.current.value

方案二:

存储节点:this.属性名=""
创建ref:ref={(当前元素名)=>{this.属性名=当前元素名}}
获取值:this.refs名(属性名).value

应用场景:(手动操作DOM) 如:上传文件

constructor(props){
        super(props)
        this.state = {
            val:'默认值'
        }
        this.inputVal = React.createRef()//创建ref
    }
    changeVal(e){
        this.setState({
            val:e.target.value,
            checked:!this.state.checked
        })
    }
    render() {
        return (
            <div>
                <h1>非受控组件</h1>
                <input defaultValue={this.state.val} ref={this.inputVal}></input>
                <br></br>
                <span>this.state.val:{this.state.val}</span>
                <br></br>
                <button onClick={this.getVal}>点击获取Input值</button>
            </div>
        )
    }
    getVal=()=>{
        //this.inputVal.current获取ref节点
        alert(this.inputVal.current.value)
    }

案例二:受控组件非受控组件的实现:

import React, { Component,Fragment } from 'react'
import './login.css'
import log from '../../assets/my.jpg'

export default class Login extends Component {
    constructor(){
        super()
        this.gpss = React.createRef()
        this.text = ''
        this.state = {
            tel:''
        }
    }
    getText(){
        console.log(this.text.value);
        console.log(this.gpss.current.value);
        console.log(this.state.tel);
    }
    changeTel(e){
        this.setState({
            tel:e.target.value
        },()=>{
            console.log(this.state.tel);
        })
    }
    render() {
        return (
            <Fragment>
                <h1>Login</h1>
                <img src={log} />
                <img src={require("../../assets/my.jpg").default} />
                <img src='logo192.png'/>
                <div>
                    <div>
                    //v是input的DOM节点
                        用户名:<input type='text' ref={(v)=>{
                            this.text = v
                        }}/>
                    </div>
                    <div>
                        密码:<input type='password' ref={this.gpss}/>
                    </div>
                    <div>
                        双向绑定:<input type='tel' value={this.state.tel} onChange={(e)=>this.changeTel(e)}></input>
                    </div>
                    <button type='button' onClick={()=>{this.getText()}}>获取值</button>
                </div>
            </Fragment>

        )
    }
}

setState

不可变值
可能是异步更新
可能会被合并

不可变值

不能直接操作state值

addCount = () =>{
   //不可变值:不能直接操作state值 
   //所以操作数组对象 不能使用push pop splice等操作
   // 数组:追加:concat,[...list,xxx] 截取:slice 过滤filter
   //对象: Object.assign({},this.state.obj1,{a:100})
   //{...this.state.obj1,a:100}
   this.setState({
       count:this.state.count+1
   })
}

可能是异步更新

render() {
        return (
            <div>
                <p>{this.state.count}</p>
                <button onClick={this.addCount}>+1</button>
            </div>
        )
    }
    addCount = () =>{
        this.setState({
            count:this.state.count+1
        })

        console.log(this.state.count)//异步拿不到最新的值
    }

在这里插入图片描述
结果:先打印再执行的count+1的操作

改为同步更新:

回调

addCount = () =>{
        this.setState({
            count:this.state.count+1
        },()=>{
        //setState的回调函数
            console.log(this.state.count)
        })  
    }

setTimeOut

setTimeout中setState是同步的:

addCount = () =>{
    setTimeout(() => {
        this.setState({
            count:this.state.count+1
        })  
        console.log(this.state.count);
    }, 0);
}

自定义的DOM事件

addCount2= ()=>{
        this.setState({
            count:this.state.count+1
        })
        console.log(this.state.count);
    }
    componentDidMount(){
        document.body.addEventListener('click',this.addCount2)
    }
    componentWillUnmount(){
        document.body.removeEventListener('click',this.addCount2)
    }

可能被合并

点击一次+1

 addCount = () =>{
        this.setState({
        //count:0+1
            count:this.state.count+1
        })  
        this.setState({
        //count:0+1
            count:this.state.count+1
        })  
         this.setState({
        //count:0+1
            count:this.state.count+1
        })  
    }

函数不合并

点击一次+3

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

组件的生命周期

在这里插入图片描述
生命周期详细文档

1)挂载
当组件实例被创建并插入 DOM 中时,其生命周期调用顺序如下:
constructor()
static getDerivedStateFromProps()
render()
componentDidMount():挂载成功后,才会产生DOM节点对象;

2)更新
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidUpdate()

3)卸载
当组件从 DOM 中移除时会调用如下方法:
componentWillUnmount()

4)错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
static getDerivedStateFromError()
componentDidCatch()

Ant Design

官网地址

npm install --save antd

路由

安装:

react-router-dom@5.2.0
+react-router@5.2.0

npm install react-router-dom react-router --save

通用组件

路由组件: BrowserRputer(history模式),需要服务器端的支持, HashRouter(hash模式);
路径匹配组件: Route ,Switch
导航组件: Link, NavLink(主要是增加激活样式);

Router路由的使用及Switch和Redirect

//index.js
import {BrowserRouter} from 'react-router-dom'

ReactDOM.render(
  //路由模式
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById('root')
);
//App.js
import {Route,Switch,Redirect} from 'react-router'

function App() {
  return (
    <div className="App">
      <section>
      {/* switch仅能有一个路由规则匹配url地址 */}
        <Switch>
        {/* exact精准匹配 */}
          <Route path='/' exact component={Login} />
          <Route path='*' component={Login} />
        </Switch>
        <Route path='/about2' component={About2} />
        <Route path='/about3' component={About3} />
        <Route path='*' component={Login} />
		//或者使用重定向
		 <Redirect to='/login' from='*'></Redirect>
      </section>
    </div>
  );
}

this.props获取路由对象

通过Route组件所匹配的规则而渲染出来的组件,可以直接通过this.props对象,获取到路由相关的三个对象:history, location, match

history:包括push(),replace(),可以实现编程式导航。即在JS代码中,实现页面的跳转。
eg:this.props.history.push(’/about’) 编程式导航实现跳转到about

location: 包括了浏览器地址栏中的的一个数据,如pathname, search;

match: params,其它的参数信息。

Link导航

import {Link} from 'react-router-dom'

<div>
   <Link to="/about">About</Link>
   <Link to="/about2">About2</Link>
   <Link to="/about3">About3</Link>
</div>

Link控制子页面显示

通过this.props.location来接收参数和其它信息

import {Link,Route} from 'react-router-dom'

<div>
    <Link to="/">Login</Link>
    //target = "_blank" 在新标签页打开
    <Link to="/about/about2" target = "_blank">About2</Link>

    <Link to={{
        pathname: "/about/about2",
        search: "?sort=name",//只能是字符串形式,可以用不问号 
        hash: "#the-hash",
        state: { id: 1 } //这是一个隐式传参。
    }}>About2 Obj</Link>

    <Link to="/about/about3">About3</Link>
</div>

<div>
    子页面:
    <Route path='/about/about3' component={About3} />
    <Route path='/about/about2' component={About2} />
</div>

注:必须要先把search中的字符串,转换成JSON对象格式再做数据处理;

    /**
     * @desc 处理search中的参数,转换成JSON数组; 
     */
  myGetPrams() {
    let search = this.props.location.search; //?uName=wqw&id=2"
    search = search.slice(1, search.length); //uName=wqw&id=2

    let arr1 = search.split("&"); // ["uName=wqw", "id=2"]

    let arr2 = [];
    arr1.forEach((item) => {
      let obj = {};
      let tempArr = [];
      tempArr = item.split("="); //   ["uName", "wqw"], ["id", "2"];
      obj[tempArr[0]] = tempArr[1]; //{uName:wqw}  {id:2}
      arr2.push(obj);
    });
    console.log(arr2);
  }

NavLink

在Link基础上,增加了激活样式。

import {Link,NavLink,Route} from 'react-router-dom'

 <div>
   <NavLink to="/about/about3/son"
    activeClassName="activetest" 
    activeStyle ={{ fontWeight: "bold",
    color: "red"}}
    >To Son1</NavLink>
    
    <NavLink to="/about/about3/son2"
     activeClassName="activetest" 
     activeStyle ={{ fontWeight: "bold",
     color: "red"}}
    >To Son2</NavLink>
</div>

<div>
    <Route path='/about/about3/son' component={Son}></Route>
    <Route path='/about/about3/son2' component={Son2}></Route>
</div>

其他属性:
exact: 表示URL和Route中的规则完全一致时,才激活样式;
strict :匹配路径后面的"/",也就是是Route path="/events/",要考虑event后面的"/"在内;
isActive:是否被激活

isActive={(match, location) => {
    return true;//一直为激活
}}

路由传参

  • 在URL?KEY=VALUE,拼接参数字符串: 明码显示参数,参数多时地址的字符串会很长很长。获得方法:this.props.location.search

  • 动态路由:id:明码显示参数,一般不会传对象,传一些简单的字符串或数字;获得方法:this.props.match.params
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • query属性:在Link中,to属性绑定一个对象,query属性的值,不会显示在地址栏中,但是刷新会丢失参数。获得方法:this.props.location.query

  • state属性:在Link中,to属性绑定一个对象,state属性的值,不会显示在地址栏中,但是刷新不会丢失参数
    获得方法:this.props.location.state

函数组件 路由传参

参考文章:react-router 中的history

import { useHistory } from "react-router-dom";
//...
const history = useHistory()

// 将新入口放入历史堆栈
history.push({
  pathname: '/the/path',
  search: '?a=query',

  // 一些不存在url参数上面的当前url的状态值
  state: { the: 'state' }
})

withRouter

在没有通过Route跳转的组件中,使用withRouter;
如APP.js中,就没有通过路由来跳转,因此要引入withRouter
在render就可以获取到this.props

import { withRouter } from "react-router";
//...
export default withRouter(App);

在高阶组件中,当调用一个高阶组件时,返回一个新的子组件,这个子组件就不是能完Route来跳转的,因此不能正常获取到history,location,match三个对象。

return (
    <>
    widthRouter("<div>子组件</div>")
    </>
)

函数组件

纯函数,输入props,输出JSX
没有实例,没有生命周期,没有state
不能扩展其他方法

函数式组件是无状态组件; class组件是有状态组件;
函数式组件中,props直接写在()中,做函数的形参; (直接注入props),获取props属性时,不写this.props,而是直接写props.属性名。或名解构成一个新的对象: {title,location,histroy,match} = props;

import React from 'react'

export default function FunctionDom(props) {
    const {list} = this.props
    // const list = [
    //     {id:1,name:'001'},
    //     {id:2,name:'002'},
    //     {id:3,name:'003'}
    // ]
    return (
        <div>
            <ul>{list.map((item)=>{
                return <li key={item.id}>{item.name}</li>
            })}</ul>
        </div>
    )
}

路由

可以通过props.history或者props.match来进行路由操作或者获取路由数据,但是没有props或者以防万一取不到props则使用useHistory, useRouteMatch:

import { useHistory, useRouteMatch } from "react-router-dom";


const history = useHistory()
const match = useRouteMatch<{ id: string }>()

const toNextPage = () =>{
	history.push(`/purch/table/${match.params.id}`)
}

props和withRouter的使用

import React from 'react'
import {withRouter} from 'react-router-dom'
export default function Fn1() {
    return (
        <div>
            <h1>
                Fn1
            </h1>
            <Mfn text='aaa'></Mfn>
        </div>
    )
}

const Ffn = (props) =>{
    console.log(props);
    return (
        <div>
            <h1>函数式组件Ffn</h1>
            <p>{props.text}</p>
        </div>
    )
}

const Mfn = withRouter(Ffn)

HOOK

useState数据更改

声明一个叫 “count” 的 state 变量
[状态属性名, 修改状态的方法] = useState(状态属性的初始值);

import React,{useState} from 'react'

export default function Fn1() {
    let [count,setCount] = useState(0);
    let [uname,setUname] = useState('zzz')
    let [uperson,setPerson] = useState({id:0,name:'ooo'})
    return (
        <div>
            <h1>Fn1--------{count}</h1> 
            <p>{uname}</p>
            <p>{uperson.name}</p>
            <button onClick={()=>{setCount(++count)}}>点击1</button>
            <button onClick={()=>{setUname('BBB')}}>点击2</button>
            <button onClick={()=>{setPerson(doObj(uperson))}}>点击3</button>
        </div>
    )
}

const doObj = (obj)=>{
    console.log(obj);
    //数组对象要做整体替换,同类组件的setState的不可变值Array.concat([])
    let nobj = Object.assign({},obj)
    nobj.name = 'ppp'
    return nobj
}

useContext数据共享

store创建React.createContext({})

//store.js
import React from 'react'
export const StoreData = React.createContext({})
export const userInfo = {
    name:'曾曾曾',
    age:23
}

父组件:StoreData.Provider value={user}传递

import React, { useState } from 'react'

import {StoreData,userInfo} from '../store/store'

import FnSon from './fnson'
import FnSon2 from './fnson2'

export default function Fn2() {
    let [user,setUser] = useState(userInfo)
    const changeName = (text) =>{
        let obj = Object.assign({},user)
        obj.name = text
        setUser(obj)
    }
    return (
        <div>
             <StoreData.Provider value={user}>
                <h1>Fn2组件</h1>
                <FnSon sonClick={changeName}></FnSon>
                <FnSon2></FnSon2>
                <button onClick={()=>changeName('我是更改的name')}>点击更改name</button>
             </StoreData.Provider>
        </div>
    )
}

子组件:useContext(StoreData)获取
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <xxx.Provider> 的 value prop 决定。

import React,{useContext} from 'react'

import {StoreData} from '../store/store'

export default function Fnson() {
    let userInfo = useContext(StoreData)
    console.log('fnson:',userInfo);
    return (
        <div>
            <h1>Son1</h1>
            <p>{userInfo.name}</p>
            <p>{userInfo.age}</p>
             <button onClick={()=>{props.sonClick('son Click')}}>子组件更改</button>
        </div>
    )
}

useEffect生命周期

该Hook 看做 componentDidMount, componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
useEffect(()=>{},[依赖])
它在第一次渲染之后和每次更新之后都会执行
允许一个组件中,写多个useEffect();根据需要不同来写。
当状态发生改变时,都会自动去调用useEffect(),执行里面的函数。
需要保证只调用一次useEffect(): 这个时候,就要使用第二个参数。如果只是执行一次,第二个参数,可是一个空数组,这样只会在挂载结束 后,执行一次;
如果第二个参数,不是空数组,而是具体的一个变量名,需要确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量,否则你的代码会引用到先前渲染中的旧变量。
通过return 返回一个回调函数来实现清除。在componentWillUnMont()时,执行回调函数。

定时器和事件的案例:

  useEffect(async () => {
    let timer;
    async function startTime() {
      timer = setInterval(() => {
        setTime(Date());
      }, 1000);
      console.log(timer);
      document.addEventListener("scroll", test, true);
    }
    await startTime();

//  清除上面实现的功能的副作用; 
    // 通过return 返回一个回调函数来实现清除。在componentWillUnMont()时,执行回调函数。
    return () => {
      clearInterval(timer);
      document.removeEventListener("scroll", test, true);
    };
  }, []);

useCallback和useRef

把普通函数变成一个缓存的函数

const changeName = useCallback((text) =>{
        let obj = Object.assign({},user)
        obj.name = text
        setUser(obj)
    },[]) 
 
const rInput = useRef()
//...
<input ref={rInput}/>

Mobx仓库

Mobx官网地址

安装: npm install mobx --save

React 绑定库: npm install mobx-react --save

安装修饰器支持的插件:npm i babel-plugin-transform-decorators-legacy -D

配置babel

git add .
git commit -m ‘xxx’
npm run eject

package.json中添加plugins项

"babel": {
    "plugins": [
      [
        "@babel/plugin-proposal-decorators",
        {
          "legacy": true
        }
      ],
      [
        "@babel/plugin-proposal-class-properties",
        {
          "loose": true
        }
      ]
    ],
    "presets": [
      "react-app"
    ]
  }

Mobx5的使用

创建store:

//userStore.js
import {observable,computed,action,makeAutoObservable} from 'mobx'

class userStoreClass{
    constructor(){
        //mobx 6.x 对所有可观察的状态(变量)进行描述:
        makeAutoObservable(this)
    }
    //写出要观察的状态
    @observable user={
        name:'admin',
        role:'管理员',
        state:0
    }

    //表示切换用户的次数
    count = 0;

    //计算属性,重写了get方法
    @computed get userName(){
        return this.user.name;
    }

    //修改state的动作
    @action.bound changeUser(){
        this.count+=1;
        if(this.count%2==0){
            this.user = {
                name:'admin',
                role:'管理员',
                state:0
            }
        }else{
            this.user = {
                name:'guest',
                role:'访客',
                state:1
            }
        }
        
    }
}

const userStore = {userInfo:new userStoreClass()}

export default userStore;

提供仓库:

import React, { Component } from 'react'

import {Provider} from 'mobx-react'
import userStore from '../../store/userStore'

import Eg1 from './eg'

export default class Mobx1 extends Component {
    render() {
        return (
            <div>
                <Provider {...userStore}>
                    <Eg1></Eg1>
                </Provider>
            </div>
        )
    }
}

Mobx参数传递及事件:

//Eg1.js
import React from 'react'
import {Link,Route} from 'react-router-dom'

import {inject,observer} from 'mobx-react'

import Demo1 from './demo1'
import Demo2 from './demo2'

function Eg1(props) {
    console.log(props);
    return (
        <div>
            <h1>Mobx Eg</h1>
            <h1>{props.userInfo.user.name}</h1>
            <Link to='/mobx/demo1'>Demo1</Link>
            <Link to='/mobx/demo2'>Demo2</Link>

            <Route path='/mobx/demo1' component={Demo1}></Route>
            <Route path='/mobx/demo2' component={Demo2}></Route>
        </div>
    )
}

export default inject('userInfo')(observer(Eg1))

函数组件和类组件中的使用:
Demo1.js类组件

import React, { Component } from 'react'

import {inject,observer} from 'mobx-react'

@inject('userInfo')
@observer

class Demo1 extends Component {
    constructor(props){
        super(props)
        this.state = {
           count:0
        }

    }

    handChange(){
        //修改mobx内的数据
        this.props.userInfo.changeUser()

        //修改组件内的状态
        let {count} = this.state
        count+=2
        this.setState({
            count
        })
    }
    render() {
        console.log('demo1:',this.props);
        return (
            <div>
                <h1>Demo1</h1>
                <p>Mobx的role:{this.props.userInfo.user.role}</p>
                <p>Mobx的count:{this.props.userInfo.count}</p>
                <p>组件内的count:{this.state.count}</p>
                <div>
                    <button onClick={()=>{this.handChange()}}>点击触发事件</button>
                </div>
            </div>
        )
    }
}

export default Demo1;

Demo2.js函数组件:

import React,{useState} from 'react'

import {inject,observer} from 'mobx-react'


function Demo2(props) {
    console.log('Demo2',props);
    let [count,changeCount] = useState(0)
    return (
        <div>
            <h1>Demo2</h1>
            <p>Mobx的role:{props.userInfo.user.role}</p>
            <p>Mobx的count:{props.userInfo.count}</p>
            <p>组件内的count:{count}</p>
            <div>
                <button onClick={()=>{changeCount(handChange(props,count))}}>点击触发事件</button>
            </div>
        </div>
    )
}

function handChange(props,count){
    //修改mobx内的数据
    props.userInfo.changeUser()
    count+=2
    return count
}


export default inject('userInfo')(observer(Demo2))

总结

创建Mobx的store:
import {observable,computed,action,makeAutoObservable} from ‘mobx’
class userStoreClass{}内再通过每一个去构建数据方法等
然后创建实例const userStore = {userInfo:new userStoreClass()}
最后export default userStore 抛出

提供Mobx
import {Provider} from ‘mobx-react’
import userStore from '…/…/store/userStore ’
Provider {…userStore} 包裹使用仓库的组件

类组件使用Mobx
import {inject,observer} from ‘mobx-react’
@inject(‘userInfo’) //依据 {…userStore}内的实例名定
@observer
class Demo1 extends Component{}
class内用this.props拿数据
export default Demo1;

函数式组件使用Mobx
import {inject,observer} from ‘mobx-react’
function Demo2(props){}
function内用props拿数据
export default inject(‘userInfo’)(observer(Demo2))

Mobx6简化版使用

不用安装babel-plugin-transform-decorators-legacy也不用配置babel

创建store.js:

import { makeAutoObservable, observable, action, computed } from "mobx";

class UserStoreClass {
  user = {
    name: "张三",
    role: "管理员",
    isGuest: "false",
  };
  count = 0;
  constructor() {
    // makeAutoObservable()不支持super()
    makeAutoObservable(this, {
      user: observable,
      count: observable,
      userName: computed,
      changeUser: action.bound, //this总是被正确地在函数内部约束。
    });
  }

  get userName() {
    return this.user.name;
  }

  changeUser(v) {
    if (this.count % 2 === 1) {
      this.user = {
        name: v,
        role: "管理员",
        isGuest: "false",
      };
    } else {
      this.user = {
        name: v,
        role: "访客",
        isGuest: "true",
      };
    }
    this.count++;
  }
}

const userStore = { userInfo: new UserStoreClass() };
export default userStore;

使用Mobx:直接引入store就可以使用了

import React from "react";
import { observer } from "mobx-react";
import store from "./../store";

const About = observer((props) => {
  return (
    <div>
      <div>{store.userInfo.userName}</div>
      <div>角色:{store.userInfo.user.role}</div>
      <Button
        onClick={() => {
          store.userInfo.changeUser("余波");
        }}
      >
        修改用户
      </Button>
    </div>
  );
});

export default About;

Redux

npm i redux
npm i react-redux
npm install @reduxjs/toolkit

HOC高阶组件

高阶组件是参数为组件,返回值为新组件的函数。
创建一个复用的按钮组件:
在这里插入图片描述

import React from 'react'
import Btneg from './Btneg'

export default function Hoc() {
    return (
        <div>
            <h1>
                HOC
            </h1>
            <Btneg name='我是HOC按钮'></Btneg>
        </div>
    )
}

//Btneg.js
import React, { Component } from 'react'
import BtnHOC from './BtnHOC'

class Btneg extends Component {
    render() {
        let {name,myClick} = this.props
        console.log(this.props);
        return (
            <div>
                <button onClick={myClick}>{name}</button>
            </div>
        )
    }
}

export default BtnHOC(Btneg)
//BtnHOC.js
import React,{Component} from 'react'

const userInfo = {
    role: 1,
    name: "普通用户",
};

function BtnHOC(DoComponet) {
   const Btn = class extends Component {
       constructor(props){
           super(props)
           this.state = {
               isShow:true,
               name:props.name
           }
       }

       doClick = () =>{
            [0,1,2,3].includes(userInfo.role)?
            console.log('true'):console.log('false');
       }

       render() {
           console.log(this.props);
           return(
            <div>
                {
                    this.state.isShow?
                    (
                       <DoComponet name={this.state.name} myClick={this.doClick}></DoComponet>
                    ):
                    (
                        <DoComponet name={this.state.name} myClick={this.doClick}></DoComponet>
                    )
                }
            </div>
           )
       }
   }

   return Btn
}

export default BtnHOC

在这里插入图片描述
总结:

使用自己封装的组件Btneg,进行传参给BtnHOC文件

Btneg设置接收HOC的按钮数据,事件等信息
export default BtnHOC(Btneg)

在HOC中接收数据进行数据事件的处理返回组件
function BtnHOC(DoComponet) {
const Btn = class extends Component{
render() {
return(
//设置组件…
)
}
return Btn
}
export default BtnHOC

Portals

组件会默认按照既定层次嵌套渲染,如何让组件渲染到父组件以外

import ReactDom from ‘react-dom’
return ReactDom.createPortal(xxx节点,document.body)

应用场景:

父组件overflow:hidden
父组件z-index值太小
fixed放第一层级时

import React, { Component } from 'react'
import ReactDom from 'react-dom'

export default class Portals extends Component {
    render() {
        console.log(this.props);
        let styleDiv = {
            position:'fixed',
            width:'100px',
            height:'100px',
            backgroundColor:'red',
            top:0,
            left:0
        }
        return ReactDom.createPortal(
            <div style={styleDiv}>
                {this.props.children} 
            </div>,
            document.body
        )
    }
}

从App内部跳到了body上
在这里插入图片描述

context

公共信息(语言主题)如何传递给每个组件?

创建:

创建一个Context,给一个默认值
const xxx = React.createContext(‘light’)
再通过xxx.Provider 提供出去,value={this.state.theme}

//创建Context默认值
const ContextTheme = React.createContext('light')
export default class ContextDemo extends Component {
    constructor(props){
        super(props)
        //主题的生产方
        this.state = {
            theme:'light'
        }
    }
    render() {
        return (
            // 传输
            <div>
                <ContextTheme.Provider value={this.state.theme}>
                    <Sonbar theme={this.state.theme}/>
                    <button onClick={this.changeTheme}>点击切换主题</button>
                    <h1>Father:{this.state.theme}</h1>
                </ContextTheme.Provider>
            </div>
        )
    }
    changeTheme =()=>{
        this.setState({
            theme : this.state.theme=='light'?'dark':'light'
        })
    }
}

第一层子组件:

function Sonbar(props) {
    return (
        <div>
            <h1>Sonbar:{props.theme}</h1>
            <GrandSon1 />
            <GrandSon2 />
        </div>
    )
}

第二次子组件接收:
类组件接收:

static contextType = xxx
或者:GrandSon.contextType = xxx
通过 this.context获取值

class GrandSon1 extends Component{
   static contextType = ContextTheme
    render(){
        return(
            <h1>GrandSon1:{this.context}</h1>
        )
    }
}

//GrandSon.contextType = ContextTheme

函数式组件接收

xxx.Consumer 包裹{value=>返回节点数据}

function  GrandSon2() {
    //函数组件没有this 通过一个函数返回
    return (<ContextTheme.Consumer>
        {value => <h1>GrandSon2:{value}</h1>}
    </ContextTheme.Consumer>)
}

在这里插入图片描述

完整js代码

import React, { Component } from 'react'

//创建Context默认值
const ContextTheme = React.createContext('light')

class GrandSon1 extends Component{
   static contextType = ContextTheme
    render(){
        return(
            <h1>GrandSon1:{this.context}</h1>
        )
    }
}

// GrandSon.contextType = ContextTheme

function  GrandSon2() {
    //函数组件没有this 通过一个函数返回
    return (<ContextTheme.Consumer>
        {value => <h1>GrandSon2:{value}</h1>}
    </ContextTheme.Consumer>)
}


function Sonbar(props) {
    return (
        <div>
            <h1>Sonbar:{props.theme}</h1>
            <GrandSon1 />
            <GrandSon2 />
        </div>
    )
}

export default class ContextDemo extends Component {
    constructor(props){
        super(props)
        //主题的生产方
        this.state = {
            theme:'light'
        }
    }
    render() {
        return (
            // 传输
            <div>
                <ContextTheme.Provider value={this.state.theme}>
                    <Sonbar theme={this.state.theme}/>
                    <button onClick={this.changeTheme}>点击切换主题</button>
                    <h1>Father:{this.state.theme}</h1>
                </ContextTheme.Provider>
                
            </div>
        )
    }
    changeTheme =()=>{
        this.setState({
            theme : this.state.theme=='light'?'dark':'light'
        })
    }
}

异步组件

import()
React.lazy
React.Suspense

import React, { Component } from 'react'

const ContextDemo = React.lazy(()=> import('./ContextDemo'))

export default class LazyDemo extends Component {
    constructor(props){
        super(props)
    }
    render() {
        return (
            <div>
                <h1>LazyDemo</h1>
                <React.Suspense fallback={<div>Loading...</div>}>
                    <ContextDemo />
                </React.Suspense>
            </div>
            //强制刷新查看loading
            //看netWork的js加载
        )
    }
}

性能优化SCU

SCU:shouldComponentUpdate 默认返回true
PureComponent 和 React.memo
不可变值immutable.js

    shouldComponentUpdate(nextProps,nextState){
        if(nextState.count!==this.state.count){
            return true //可渲染
        }
        return false //不可渲染
    }

react 默认父组件有更新,子组件无条件也会更新
配合setState不可变值使用

PureComponent和memo

函数组件:PureComponent,SCU中实现了浅比较
在这里插入图片描述

immutable

基于共享数据(不是深拷贝),速度好
在这里插入图片描述

关于组件公共逻辑的抽离

高阶组件HOC
Render Props

import React, { Component } from 'react'

const withHoc = (Mouse)=>{
    class withHocComponent extends Component {
        constructor(props){
            super(props)
            this.state = {
                x:0,
                u:0
            }
            this.move = this.move.bind(this)
        }
        move(e){
            this.setState({
                x:e.clientX,
                y:e.clientY
            })
        }
        render() {
            return (
                <div onMouseMove={this.move} style={{border:'1px solid red',height:'50px'}}>
                    <Mouse {...this.props} mouse={this.state}/>
                </div>
            )
        }
    }
    return withHocComponent

}
const Mouse = (props) =>{
    const {x,y} = props.mouse
    return (
        <div style={{border:'1px solid black'}}>
            <h1>Postion:{x},{y}</h1>
        </div>
    )
}

export default withHoc(Mouse)

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Da Zeng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值