【React】学习笔记

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

jsx表达式

原生语法

function LikeButton() {
    return React.createElement(
        'button', {
            onClick: () => {
                // 执行函数
            }
        },
        '按钮名称'
    );
}
ReactDOM.render(React.createElement(LikeButton), document.getElementById('root'));
             // 👆React.createElement(LikeButton)  ===   LikeButton();

在这里插入图片描述

React16.2的fiber架构

jsx语法

@babel/preset-react

function LikeButton() {
    return <button onClick={() => { }}>按钮名称</button>
} 
ReactDOM.render(
    <LikeButton />,
    document.getElementById('root')
);

CSS Module

CSS Module 戳这里

import React from 'react';
import style from './App.css';
export default () => {
    return (
        <h1 className={style.title}>
            Hello World
        </h1>
    );
};
// 编译完
<h1 class="_3zyde4l1yATCOkgn-DBWEL">
    Hello World
</h1>

VUE scope
Angular encapsulation:ViewEncapsulation.Native

创建组件的两种方式

class组件(有状态组件

有状态组件主要用来定义交互逻辑和业务数据,使用{this.state.xxx}的表达式把业务数据挂载到容器组件的实例上,然后传递props到展示组件,展示组件接收到props,把props塞到模板里面。

class Welcome extends React.Component {
    constructor(props) {
        super();
        this.state = {
            counter: 1,
        }
    }
    render() {
        return <h1>Hello, {this.props.name}</h1>;
    }
}
State

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。

this.setState({
    counter: 12
});
this.setState((prevState, props)  => ({
    counter: prevState.counter + props.increment
}));

实现原理

const queue = [];
function setState( stateChange ) {
    enqueueSetState( stateChange, this );
}
function enqueueSetState( stateChange, component ) {
    if ( queue.length === 0 ) { // 如果queue的长度是0,也就是在上次flush执行之后第一次往队列里添加
        defer( flush );
    }
    queue.push( {
        stateChange,
        component
    } );
}
// 清除队列函数
function flush() {
    let item;
    while( item = queue.shift() ) {
        const { stateChange, component } = item;
        if ( !component.prevState ) {  // 如果没有prevState,则将当前的state作为初始的prevState
            component.prevState = Object.assign( {}, component.state );
        }
        if ( typeof stateChange === 'function' ) { // 如果stateChange是一个方法,也就是setState的第二种形式
            Object.assign( component.state, stateChange( component.prevState, component.props ) );
        } else { // 如果stateChange是一个对象,则直接合并到setState中
            Object.assign( component.state, stateChange );
        }
        component.prevState = component.state;
    }
}
function defer( fn ) {
    return Promise.resolve().then( fn );
}
生命周期

componentWillMount 在渲染前调用,在客户端也在服务端。
(类比 created() 、 ngOnInit())

componentDidMount 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
(类比 mounted() 、 ngAfterContentInit())

componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
(类比 updated() 、 ngOnChanges())

shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。

componentWillUpdate 在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。

componentWillUnmount 在组件从 DOM 中移除的时候立刻被调用。
(类比 destroyed() 、 ngOnDestroy())

refs获取DOM节点
myInput = React.createRef();

<input ref={this.myInput} />
// this.refs.myInput

<input ref={ref => this.myInput = ref} />
// this.myInput

createRef源码

function createRef() {
    var refObject = {
        current: null
    };
    {
        Object.seal(refObject);
    }
    return refObject;
}
当组件挂载时,将 DOM el元素传递给 ref 的回调
当组件卸载时,则会传递 null

函数组件(无状态组件

接收来自父组件props传递过来的数据,使用{props.xxx}的表达式把props塞到模板里面。无状态组件应该保持模板的纯粹性,以便于组件复用。

function Welcome(props) {
    // props接受父组件传参
    return <h1>Hello, {props.name}</h1>;
}
Hook

Hook 可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

import React, { useState, useEffect, useRef } from 'react';
function Example() {
    const [count, setCount] = useState(0);
    const pRef = useRef();
    // pRef.current
    useEffect(() => {
        // 更新DOM之后调用 相当于componentDidMount
        return () => {
            // 销毁时调用 相当于componentWillUnmount
        }
    },[count]);
    // 没传第二个参数 useEffect执行函数被循环执行
    // 第二个参数如果只传一个空数组,函数只会在组件挂载时执行一次 ,相当于 componentDidMount
    // 第二个参数不为空数组,数组里数值改变触发函数执行(销毁再加载

    return (
        <React.Fragment>
            <p ref={ pRef }>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
        </React.Fragment>
    );
}
export default Example
useState实现原理
let index = 0;
let stateMap = new Map();
function useState(value) {
    stateMap.set(index, stateMap.get(index) || value);
    let currentIndex = index;
    function fun(value) {
        stateMap.set(currentIndex, value);
        Render();
    }
    return [stateMap.get(index++), fun];
}
useEffect实现原理
let _deps; // _deps 记录 useEffect 上一次的 依赖
function useEffect(callback, depArray) {
    const hasNoDeps = !depArray; // 如果 dependencies 不存在
    const hasChangedDeps = _deps
        ? !depArray.every((el, i) => el === _deps[i]) // 两次的 dependencies 是否完全相等
        : true;
    if (hasNoDeps || hasChangedDeps) { // 如果 dependencies 不存在,或者 dependencies 有变化
        callback();
        _deps = depArray;
    }
}

React hooks之useState,useReducer,useEffect原理分析
跨渲染周期存储数据

useRef

跟refs原理当差不差

useContext

useContext 接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 的 value prop 决定。
当组件上层最近的 更新时,该 Hook 会触发重渲染,并使用最新传递给 provider 的 context value 值。

条件渲染、列表

render() {
    let { dataList, flag } = this.state;
    return (<React.Fragment>
        {
            dataList.map(d => (<li key={d.id}>{d.name}</li>))
        }
        {
            flag ? <span>true</span> : <span>false</span>
        }
    </React.Fragment>)
}

事件处理

1. 法1

constructor() {
    super();
    this.handleClick = this.handleClick.bind(this);
}
render(){
    return <button onClick={this.handleClick}>按钮</button>
}

2. 法2

handleClick = () => { //执行内容 }
render(){
    return <button onClick={this.handleClick}>按钮</button>
}

3. 法3

handleClick(){ //执行内容 }
render(){
    return <button onClick={() => this.handleClick()}>按钮</button>
}

错误写法1:

handleClick(){ // 不会被调用 }
render(){
    return <button onClick={this.handleClick()}>按钮</button>
}

原因:
JSX解析过程{}中函数代码会被执行,点击不会执行handleClick。
错误写法2:

handleClick(){ // this 丢失 }
render(){
    return <button onClick={this.handleClick}>按钮</button>
}

原因:
在JSX中传递的事件不是一个字符串,而是一个函数(如:onClick={this.handleClick}),此时onClick即是中间变量,所以处理函数中的this指向会丢失。解决这个问题就是给调用函数时bind(this),从而使得无论事件处理函数如何传递,this指向都是当前实例化对象。

通信

props

父=>子

// 父组件
class Parent extends React.Component {
    constructor() {
        super();
    }
    render() {
        return (
            <Child msg='msg!!!' />
        )
    }
}
// 子组件(函数式
function Child(porps) {
    let { msg } = porps
    return <p>父组件入参:{msg}</p>
}
// 子组件(class组件
import PropTypes from 'prop-types'; 
class Child extends React.Component {
    constructor(props) {
        super();
    }
    render() {
        let { msg } = this.props;
        return <p>父组件入参:{msg}</p>
    }
}
// 限制参数类型、设置参数默认值
Child.propTypes = {
    msg: PropTypes.string.isRequired, // isRequired是否必传
}
Child.defaultProps = {
    msg: '默认msg'
}

子=>父

// 父组件
class Parent extends React.Component {
    constructor() {
        super();
        this.state = {
            childMsg: ''
        }
    }
    getChildMsg = (val) => {
        this.setState({
            childMsg: val
        })
    }
    render() {
        let { childMsg } = this.state;
        return (
            { childMsg }
            <Child handleClick={ this.getChildMsg } />
        )
    }
}
// 子组件
function Child(porps) {
    let { handleClick } = porps;
    return <button onClick={ () => handleClick('childMsg') }>按钮</button>
}

context

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。

// 创建一个上下文的容器(组件), defaultValue可以设置共享的默认数据
const ContextDemo = React.createContext(defaultValue);
<ContextDemo.Provider value={/*共享的数据*/}>
    /*里面可以渲染对应的内容*/
</ContextDemo.Provider>

createContext实现原理

const emitter = {
    listeners: [],
    on: fn => {
        emitter.listeners.push(fn);
    },
    off: fn => {
        emitter.listeners.splice(emitter.listener.findIndex(fn), 1);
    },
    emit: value => {
        emitter.listeners.forEach(fn => fn(value));
    }
};
function createContext(defaultValue) {
    class Provider extends React.Component {
        componentDidUpdate() {
            emitter.emit(this.props.value);
        }
        componentDidMount() {
            emitter.emit(this.props.value);
        }
        render() {
            return this.props.children;
        }
    }
    class Consumer extends React.Component {
        constructor(props) {
            super(props);
            this.state = { value: defaultValue };
            emitter.on(value => {
                this.setState({ value }); 
            });
        }
        render() {
            return this.props.children(this.state.value);
        }
    }
    return { Provider, Consumer };
}

接收context的2种方式:Consumer 和 contextType
1. 法1

class ComponentA extends React.Component {
    render() {
        <ContextDemo.Consumer>
            {value => /*根据上下文  进行渲染相应内容*/}
        </ContextDemo.Consumer>
    }
}
// 同一子组件若要接收多个context,可以使用Consumer方式

2. 法2

class ComponentA extends React.Component {
  render() {
    let value = this.context;
    return {value => /*根据上下文  进行渲染相应内容*/}
  }
}
ComponentA.contextType = ContextDemo;  // ContextDemo => FilberNode

2. 法3

function ComponentA() {
    let context = useContext(ContextDemo);
    return (
        <p>{context.id}</p>
    )
} 

父子组件、同级组件通信

import React from "react";
import PropTypes from "prop-types";
class Child1 extends React.Component {
    constructor(props, context) {
        super();
    }
    render() {
        return (
            <div>
                <p>{this.context.msg}</p>
                <button onClick={() => this.context.callback(new Date().getTime())}>按钮</button>
            </div>
        )
    }
}
Child1.contextTypes = {  // 子组件需要通过 contextTypes 指定需要访问的元素
    msg: PropTypes.string,
    callback: PropTypes.func
}
function Child2(props, context) {
    return (<div>
        <p>{context.msg}</p>
        <button onClick={() => context.callback(Math.round(Math.random() * 100))}>按钮</button>
    </div>)
}
Child2.contextTypes = {
    msg: PropTypes.string,
    callback: PropTypes.func
}
export default class ParentComponent extends React.Component {
    constructor() {
        super();
        this.state = {
            msg: "msg"
        };
    }
    getChildContext() { // getChildContext 传递给子组件的属性值
        return {
            msg: this.state.msg,
            callback: this.callback
        }
    }
    callback = (val) => {
        this.setState({
            msg: `msg${val}`
        })
    }
    render() {
        return (<div>
            <p>msg:{this.state.msg}</p>
            <Child1 />
            <Child2 />
        </div>)
    }
}
ParentComponent.childContextTypes = { // 传递给子组件的属性需要先通过 childContextTypes 来指定
    msg: PropTypes.string,
    callback: PropTypes.func
};

React-Redux

// store.js
import { createStore } from 'redux';
// state
const defaultState = {
    str: '这是字符串'
}
// store 
const store = createStore( // 接受一个Reducer函数,Reducer函数接受的是state和actions
    (state = defaultState, action) => {
        switch (action.type) {
            case 'change_str':
                state.str = '时间戳:' + action.value
                break;
            default:
                break;
        }
        return { ...state };
    }
)
export default store;

// 组件调用
import React from 'react';
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
    return {
        str: state.str,
    }
}
const mapDispatchToProps = (dispatch) => {
    return {
        resetStr: (data) => dispatch({
            type: 'change_str',
            value: data
        }),
    }
}
const Child = function (props) {
    return <React.Fragment>
        <button onClick={() => props.resetStr(new Date().getTime())}>{props.str}</button>
    </React.Fragment>
}
export default connect(
    mapStateToProps, // 第一步
    mapDispatchToProps // 第二步
)(Child);

// 调用的组件需要被Provider包裹 通常放在App.js
import { Provider } from 'react-redux'
<Provider store={store}>
    <Child />
</Provider>

createStore

function createStore(reducer){
    let state = null;
    const listenerArr = [];
    const getState = () => state;
    const dispatch = (action) => {
        state = reducer(state, action)
        listenerArr.forEach(listener => listener())
    }
    const subscribe = (listener) => listenerArr.push(listener)
    // 这里初始化dispatch的原因是在这之前,state是为null的
    // 所以我需要传一个不存在的action去reducer里面,拿到最默认的那个defaultState
    // 这个defaultState写在reducer的那个文件里面
    dispatch({});
    return {
        dispatch,
        subscribe,
        getState,
    }
}

Provider

import React, {Component} from 'react'
import {PropTypes} from 'prop-types'
export default class Provider extends Component {
    getChildContext() {
        return {store: this.props.store}
    }
    constructor() {
        super()
        this.state = {}
    }
    render() {
        return this.props.children
    }
}
Provider.childContextTypes = {
    store: PropTypes.object
}

connect

import {Component} from "react";
import React from "react";
import {PropTypes} from 'prop-types'
const connect = (mapStateToProps, mapDispatchToProps) => (WrappedComponent => {
    class Connect extends Component {
        constructor() {
            super()
            this.state = {}
        }
        componentWillMount() {
            this.unSubscribe = this.context.store.subscribe(() => {
                this.setState(mapStateToProps(this.context.store.getState()))
            })
        }
        componentWillUnmount() {
            this.unSubscribe()
        }
        render() {
            return <WrappedComponent  {...this.state}
                                      {...mapDispatchToProps(this.context.store.dispatch)}/>
        }
    }
    Connect.contextTypes = {
        store: PropTypes.object
    }
    return Connect
})
export default connect

redux-thunk
Redux store 仅支持同步数据流。使用 thunk 等中间件可以帮助在 Redux 应用中实现异步性。可以将 thunk 看做 store 的 dispatch() 方法的封装器;我们可以使用 thunk action creator 派遣函数或 Promise,而不是返回 action 对象。

插槽

import React from "react";
class ChildA extends React.Component {
    constructor(props) {
        super();
    }
    render() {
        let { slotComp } = this.props
        return (<React.Fragment>
            <div>
                {slotComp}
            </div>
        </React.Fragment>)
    }
}
function ChildB(props) {
    return props.children
}
export default class Parent extends React.Component {
    constructor() {
        super();
    }
    Comp() {
        return <span>slot1.0</span>
    }
    render() {
        return (<React.Fragment>
            <ChildA slotComp={this.Comp()} />
            <ChildB>
                <span>slot2.0</span>
            </ChildB>
        </React.Fragment>)
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值