React

168 阅读7分钟

React组件间通信

父子组件传值(props)
父组件 -> 子组件
    父组件<son value={this.state.value} />
    子组件{this.props.value}
子组件 -> 父组件
    子组件this.props.onClick()
    父组件<son onClick={this.onClick.bind(this)} />
        onClick(e){
            this.setState({
                value: e
            })
        }
跨层级组件通信(React.createContext())
//文件context.js
import React from 'react'
let { Consumer, Provider } = React.createContext(null) //创建context并暴露Consumer和Provider
export { Consumer, Provider }

//父组件
import { Provider } from './context.js'
<Provider value={value}>
    <son />
</Provider>

//子组件
import { Consumer } from './context.js'
<Context>
    {(value) => {
        <div>父组件的值:{value}</div>
        <grandson />
    }}
</Context>

//孙组件
import { Consumer } from './context.js'
<Consumer>
    {(value) => {
        <div>父组件的值:{value}</div>
    }}
</Consumer>
父组件获取子组件实例从而调用子组件方法(OnRef、props)
//父组件
child: any
sonRef = (ref) => {
    this.child = ref
}
this.child.onClickSon()
<Son onRef={this.sonRef}></Son>

//子组件
componentDidMount() {
    this.props.onRef(this) // 在这将子组件的实例传递给父组件this.props.onRef()方法
}
onClickSon = ()=>{
    this.setState({
        value: ''
    })
}
访问DOM节点或者组件(ref)
//父组件
son: any
this.son = React.createRef() // 在此处创建ref
onClick = () => {
    const { current } = this.son  // 注意,这里必须通过 this.son.current拿到子组件的实例
    current.onClickSon()
}
<son ref={this.son}></son>

//子组件
onClickSon = () => {
    alert()
}

React生命周期

react的生命周期主要分为四个阶段:组件初始化、组件挂载、组件更新、组件卸载。

组件初始化阶段:

constructor()完成了React数据的初始化。

组件挂载阶段:

componentWillMount(){}主要用于服务端渲染,修改state的值。
render()页面渲染执行逻辑,render()函数把jsx编译为函数并生成虚拟Dom,只能通过this.props和this.state访问数据,且不能执行this.setState更改组件状态。
componentDidMount(){}组件挂载到Dom后调用,且只调用一次。可以在函数中通过this.refs获取真实Dom。

组件更新阶段:

componentWillReceiveProps(nextProps){}接收父组件新的props时通过this.setState更新state重新渲染组件执行的逻辑。
shouldComponentUpdate(nextProps,nextState){}在setState以后,state发生变化,组件会重新渲染执行逻辑,如果在这个生命周期中return false可以阻止组件的更新,主要用于性能优化。return true则会更新组件。
componentWillUpdate(nextProps,nextState){}在shouldComponentUpdate方法返回true后,组件将重新渲染调用该函数(此时不更新props和state,否则会陷入死循环),之后调用render()方法进行重新渲染。
componentDidUpdate(prevProps,prevState){}可以获取操作更新后的Dom节点。

组件卸载阶段:

componentWillUnmount(){}组价卸载前被调用,可执行清除定时器和组件的监听器等操作。

//父组件
class Father extends React.Component {
    static defaultProps = {
        //1.加载默认静态属性
    };
    
    constructor() {
        super()
        //2.加载默认状态,接收父组件传下来的props和定义this.state初始内容
        this.state = {num: 1}
    }
    
    componentWillMount() {
        //3.父组件挂载前
    }
    
    componentDidMount() {
        //5.父组件挂载完成
    }
    
    shouldComponentUpdate() {
        //6.父组件是否需要更新
        return true 更新组件
        return false  不更新组件
    }
    
    componentWillUpdate() {
        //7.组件将要更新
    }
    
    componentDidUpdate() {
        //8.父组件更新完成
    }
    
    onClick = ()=>{
        this.setState({
            num: this.state.num + 1
        })
    };
    
    render() {
        4.render父组件挂载
        return (
            <div>
                <a onClick={this.onClick}></a>
                <Child />
            </div>
        )
    }
}
ReactDOM.render(<Father/>,document.getElementId('root'));

//子组件
class Child extends React.Component {
    componentWillReceiveProps() {
        //9.子组件将要接收到新属性
    }
    
    shouldComponentUpdate(newProps,newState) {
        //10.子组件是否需要更新
        return true 更新组件
        return false  不更新组件
    }
    
    componentWillUpdate() {
        //11.子组件将要更新
    }
    
    componentDidUpdate() {
        //13.子组件完成更新
    }
    
    componentWillUnmount() {
        //14.子组件将卸载
    }
    
    render() {
        //13.子组件挂载中
        return (
            <p>this.props.num</p>
        )
    }
}

React路由

React提供了react-router来管理路由,react-router包含3个分别为react-router是router的核心代码、react-router-dom是用于浏览器和react-router-native是用于原生应用。

react-router有3种基础组件

路由组件:BrowserRouter 和 HashRouter

这两个路由都会创建一个专门的history对象,两者之间的主要区别是他们存储URL和与Web服务器通信的方式,是否带#。BrowserRouter使用了history模式,HashRouter使用了hash模式。

路由匹配组件:Switch 和 Route

Route组件是用于路径的匹配,path属性:用户设置匹配到的路径。component属性:设置匹配到的路径后,渲染的组件。exact属性:精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件。
Switch组件匹配path对应组件,Route组件匹配到都会渲染,而Switch组件包裹只匹配第一个。

导航组件:Link, NavLink, 和 Redirect

Link组件用于路径跳转,渲染成a元素,属性to用于跳转的路径。
NavLink组件匹配路径选中,修改a元素的样式。属性activeStyle:匹配到的样式,属性activeClassName匹配到的类名class,属性exact:是否精准匹配。
Redirect组件是用于路由的重定向,当这个组件出现后,就会执行跳转对应的to路径中。

Redux

Redux是一个可预测的JavaScript应用状态管理容器,将整个应用的状态存储到store里。组件改变state的方法是通过调用store的dispatch方法,触发action,这个action被对应的reducer处理。组件派发(dispatch)行为(action)给store,而不是直接通知其他组件。其他组件可以通过订阅store中的状态(state)来刷新自己的视图。

Redux使用教程(Provider、reateStore、connect)

//安装redux、react-redux
npm install redux --save
npm install react-redux 
初始代码模版index.js文件
//引入createStore、Provider
import { createStore } from 'redux' 
import { Provider } from 'react-redux' 
import App from "./App.js"

//创建Store的初始化数据
const initialState = {value: ""} 

//接收state和action然后return state数据
const rootReducer = (state,action) => {  
    return state
}

//传入rootReducer和initialState生成store
const store = createStore(rootReducer,initialState) 

//store作为参数是Redux在React使用的绑定库,搭建Redux和React交流的桥梁。
ReactDOM.render(
    <Provider store={store}>
        <App />
    </Provider>
    document.getElementById("root")
)
App.js组件使用
//导入connect函数
import React from "react"
import {connect} from "react-redux"
import todo from "./todo.js"

class App extends React.Component {
    //接收store数据
    constructor(props) {
        super(props)
    }
    
    render() {
        return (
            <div>{this.props.value}</div>
            <todo></todo>
        );
    }
}
const mapStateToProps = (state,props) => ({
    value: state.value
})
export default connect(mapStateToProps)(App)

connect是一个高阶函数,高阶函数是指可以接受参数调用并返回另一个函数的函数。connect通过调用函数返回新函数再接收App组件作为参数,通过mapStateToProps注入value属性,再注入App组件。
将Redux和React之间的数据互通,并删除了this.state,使store的状态替代了this.state,因此this.state.value会报错。

Action Creators

Action是更新Store的状态的一种方式:通过调用dispatch函数,传递一个action给这个函数。
Action是一个简单的JavaScript对象,它包含type类型(必有属性),以及更新的状态需要的数据。{type:'do',value: '内容'}

//actions.js文件
export const doChange = value => ({
    type: 'do',
    value,
})

//todo.js
import React from "react"
import {connect} from "react-redux";
import {doChange} from "./actions"

const todo = ({dispatch}) => {
    return (
        <button onClick={e => {
            e.preventDefault();
            dispatch(doChange('改变数据'));
        }}>todo</button>
    )
}

export default connect()(todo)
Reducer:响应Action的指令

Reducer响应从组件中dispatch出来Action,并更新Store中的状态。
Reducer是一个普通的JavaScript函数,它接收两个参数: state是Store中的对象状态树和action是组件中dispatch的那个Action。

reducer(state,action){
    //对state进行操作
    //state = {a:1,b:2}修改方式 newState = {...state,a: 3}得出 {a:3,b:2}
    return newState
}
//在初始代码模版index.js文件
const rootReducer = (state,action) => {
    switch (action.type) {
        case "do": {
            return {
                ...state,
                value: action.value
            }
        }
        default: return state
    }
}

reducer的约定是一个纯函数,不能直接修改state,而是使用{...}对对象解构对的方式返回一个被修改后的state。

JSX

JSX是JavaScript XML的缩写,也是JavaScript的语法扩展是React使用的一种文件,它利用JavaScript的表现力和类似HTML的模版语法,这使得HTML文件非常容易理解,此文件能使应用非常可靠,并能够提高其性能。浏览器不能读取常规JavaScript对象中的JSX,需要使用Babel转换器将JSX文件转换成JavaScript对象,然后才能让浏览器解析。

Virtual Dom虚拟Dom

Virtual DOM是一个轻量级的JavaScript对象,它最初只是real DOM的副本。它是一个节点树,他将元素、他们的属性和内容作为对象及其属性。React的渲染函数从React组件中创建一个节点树。然后它响应数据模型中的变化来更新该树,该变化是由用户或系统完成的各种动作引起的。

Virtual DOM工作流程
  1. 每当低层数据发生变化时,整个UI都将在Virtual DOM描述中重新渲染。
  2. 然后计算之前DOM表示与新表示的之间的差异
  3. 完成计算后,将只用实际更改的内容更新real DOM。

React知识总结

副文本HTMl解析dangerouslySetInnerHTML
<div dangerouslySetInnerHTMl={value}></div>
两种事件绑定
<button onClick={this.click.bind(this,value1,value2)}></button>
<button onClick={() => this.click(value1,value2)}></button>
click(data1,data2){
    console.log(data1,data2)
}
setState()不可变值、可能是异步更新、可能会被合并
数组不能使用push()、pop()、splice()等方法,这样违反了不可变值,会影响 shouldCompententUpdate判断
this.setState(() => ({
    list: this.state.list.concat(array), //追加
    list: [...this.state.list,array], //追加
    list: this.state.list.slice(0,3), //截取
    list: this.state.list.filter(item => item < 100) //筛选
}))
对象修改值
this.setState(() => {
    obj: Object.assign({},this.state.obj,object),
    obj: {...this.state.obj,...obj}
})
通常情况下为异步更新值
const count = this.state.count + 1
this.setState({
    count
},() => {
    //这个函数没有默认参数
    //类比Vue的$nextTick()
    console.log(this.state.count)//打印更新后的值
})
【重点】传入对象会被合并,结果只执行一次
初始值this.state.count = 0
this.setState({count:this.state.count+1})
this.setState({count:this.state.count+1})
this.setState({count:this.state.count+1})
输出值:this.state.count = 1
【重点】传入函数不会合并,函数无法合并
初始值this.state.count = 0
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
this.setState((prevState, props) => {
    return {
        count: prevState.count + 1
    }
})
输出值:this.state.count = 3