React & Redux基础知识补遗

 

0x0 React基础知识部分

React的定位:视图层框架

当紫色的组件,希望和右边灰色的组件通信的时候,如果只有React框架:

React把自己定位为「视图层框架」:

State、Props、render之间的关系

生命周期方法

componentWillReceiveProps执行时机

componentDidMount执行网络请求

虚拟DOM

虚拟DOM是RN的基础:

虚拟DOM机制导致最终的渲染步骤非常少

  1. 把虚拟DOM这个JS对象渲染到DOM中,就是React
  2. 渲染到Native端生成一个Native组件,就是RN

setState异步执行

setState是异步的,连续调用3次state,实际上会只进行一次DOM比对

this.setState(() => ({
  stateKey1: stateValue1,
  stateKey2: stateValue2,
}), () => {
  // setState异步执行完成后的回调函数
})


Diff算法

differ算法的本质:

虚拟dom会给每个dom一个key,diff的时候相同key的进行比对,提升比对性能

数组型元素,不能用index作为key值的原因:

Ref的使用

// 设置
<input
  ref={(input) => {this.input = input}}
/>

// 使用 xxxfunc() { const value = this.input.value … }

列表拷贝

const newList = […oldList]

动画相关

方法一:通过切换组件的className控制动画

.show {
  opacity: 1;
  transition: all 1s ease-in;
}

.hide { opacity: 0; transition: all 1s ease-in; }

// 调用 … render() { <div className={this.state.show ? ‘show’ : ‘hide’} /> } …

方法二:css3关键帧动画

@key-frames hide-item {
  0% {
    opacity: 1;
    color: red;
  }
  50% {
    opacity: 0.5;
    color: green;
  }
  100% {
    opacity: 0;
    color: blue;
  }
}

.hide { animation: hide-item 2s ease-in forwards; // forwards关键字:动画结束之后保存css样式 }

说明:百分比,是以「时间」为维度的

方法三:react-transition-group实现动画

0x1 Redux基础知识部分

1 Redux的简单使用

Redux思路

将数据放在公共的数据存储区Store中,需要传值的时候,修改Store中的内容

Redux数据流

Redux使用的一般步骤

创建store

/src/store/index.js

import { createStore } from ‘redux’
import reducer from ‘./reducer’

const store = createStore(reducer, window.REDUX_DEVTOOLS_EXTENSION && window.REDUX_DEVTOOLS_EXTENSION_COMPOSE // 配置chrome devTools插件 );

export default store;

说明:

  1. chrome的redux中间件插件配置,文档:https://github.com/zalmoxisus/redux-devtools-extension

/src/store/reducer.js

const defaultState = {
    inputValue: ‘ttt’,
    list: [1, 2],
};

export default (state = defaultState, action) => { return state; }

store的基本使用

import store from ‘./store’

class App extends Component { constructor(props) { super(props); this.state = store.getState(); } … }

action的创建和使用

App.js

handleEvent(e) {
    const action = {
        type: ‘event_name’,
        value: e.target.value,
    };
    store.dispatch(action)
}

根据action调整reducer

const defaultState = {
    inputValue: ‘ttt’,
    list: [1, 2],
};

export default (state = defaultState, action) => { if (action.type === ‘event_value’) { const newState = JSON.parse(JSON.stringify(state)); newState.inputValue = action.value; return newState; } return state; }

注意:

  1. reducer的写法:只能创建一个新的state,不能改变state的属性值


组件订阅store改变事件

App.js

store.subscribe(this.handleStoreChange);


抽取文件管理所有action

actionTypes.js

export const EVENT_NAME = ‘event_name’;


抽取文件管理action的创建

actionCreators.js

getEventAction

Redux使用三原则

  1. store是唯一的
    • 整个app只能有一个store,表面上时reducer改变了store的内容,其实不是,reducer只是返回了一个新的state给store,最终stroe改变自己的内容
  2. 只有store能改变自己的内容
  3. Reducer必须是纯函数
    • 给定输入一定有指定的输出

Redux相关API

createStore

store.dispatch

store.getState

store.subscribe

UI组件、容器组件、无状态组件

UI组件:只负责渲染UI

容器组件:负责整个组件的功能实现 & 数据传递的

无状态组件:一个组件只有render函数的时候,可以使用纯函数实现,叫无状态组件,没有生命周期方法,性能更优秀

const component = (props) => {
    return (
        <div>xxx</div>
    )
}


2 Redux中间件

中间件:插入到xx和xx之间的东东

Redux中间件:本质是对dispatch方法的封装

Redux-thunk

异步请求放在组件中做,组件会很臃肿 - Redux-thunk可以让这些逻辑放到action中

相关文档

未使用Redux-thunk时的网络请求,一般写在App.js文件中

componentDidMount() {
    axios.get(‘xxx’).then((res) => {
        const action = xxxAction(res.data)
        store.dispatch(action)
    })
}


redux-thunk配置

相关文档:https://github.com/zalmoxisus/redux-devtools-extension#12-advanced-store-setup

import { createStore, applyMiddleware, compose } from ‘redux’
import reducer from ‘./reducer’
import thunk from ‘redux-thunk’

const composeEnhancers = window.REDUX_DEVTOOLS_EXTENSION_COMPOSE || compose; const enhancer = composeEnhancers( applyMiddleware(thunk), ); const store = createStore(reducer, enhancer);

export default store;


之后App.js

componentDidMount() {
    const action = xxxAction()
    store.dispatch(action)
}

说明:

之后的Action

export const xxxAction = () => {
    return (dispatch) => {
        axios.get(‘xxx’).then((res) => {
            const action = xxxAction(res.data)
            dispatch(action)
        })
    }
}

Redux-saga

文档

这个东东和thunk二选一就好,作用是,可以把异步逻辑抽离到一个单独的文件中管理

之前的App.js

componentDidMount() {
    axios.get(‘xxx’).then((res) => {
        const action = xxxAction(res.data)
        store.dispatch(action)

})

}

saga.js

import { takeEvery, put } from ‘redux-saga/effects’
import { EVENT_NAME } from ‘./actionTypes’
import { getEventAction } from ‘./actionCreators’
import axios from ‘axios’

function* getXXXRequest() { try { const res = yield axios.get(‘xxx/xxx’); // #1 const action = getEventAction(res.data); yield put(action); // #2 } catch (e) { console.log(‘失败’); } }

function* mySaga() { yield takeEvery(EVENT_NAME, getXXXRequest); // #3 }

export default mySaga;

说明:

  1. 这样写异步请求:yield关键字 - 异步请求执行完之后,再赋值给res
    1. 异步请求执行完之后,再执行put方法 - 以及处理网络请求失败
    2. yield关键字是generator函数相关的语法
  2. saga内置的put函数可以实现把action丢给store - generator中不要用promise做异步处理
  3. takeEvery可以捕获action & 调用自定义方法


配置/store/index.js

import { createStore, applyMiddleware, compose } from ‘redux’
import reducer from ‘./reducer’
import createSagaMiddleware from ‘redux-saga’
import mySagas from ‘./saga’

const sagaMiddleware = createSagaMiddleware(); const composeEnhancers = window.REDUX_DEVTOOLS_EXTENSION_COMPOSE || compose; const enhancer = composeEnhancers( applyMiddleware(sagaMiddleware), ); const store = createStore(reducer, enhancer); sagaMiddleware.run(mySagas);

export default store;

说明:

3 React-Redux

入口文件:src/index.js

import React from ‘react’;
import ReactDOM from ‘react-dom’;
import XXComponent from ‘./XXComponent’;
import { Provider } from ‘react-redux’
import store from ‘./store’

const App = ( <Provider store={store}> <XXComponent /> </Provider> );

ReactDOM.render(App, document.getElementById(‘root’));

说明:

  1. Provider组件 - react-redux提供的核心组件,Provider会把指定的store提供给里面所有的组件及子组件

组件:XXComponent.js

import React from ‘react’;
import { connect } from ‘react-redux’

// class XXComponent extends Component { // #4 // render() { // return ( // <div> // <button onClick={this.props.clickEvent}> // {this.props.oneProp} // </button> // </div> // ) // } // }

const XXComponent = (props) => { // #4 const {clickEvent, oneProp} = props return ( <div> <button onClick={clickEvent}> {oneProp} </button> </div> ) };

const mapStateToProps = (state) => { // #2 return { oneProp: state.oneValue } };

const mapDispatchToProps = (dispatch) => { // #3 return { clickEvent(e) { const action = { type: ‘some_action’, value: e.target.value, }; dispatch(action) } } };

export default connect(mapStateToProps, mapDispatchToProps)(XXComponent); // #1 #2 #3 #5

说明:

  1. 通过connect方法,让组件和store做连接
  2. mapStateToProps是用来把store转为当前组件的props的
  3. mapDispatchToProps,可以把其中定义的函数,绑定给组件的prop中。组件直接调用this.props.xxxxx - 这里可以接收到dispatcher

  4. 由于业务逻辑被抽离了,组件可以做一个「无状态组件
  5. connect返回的内容,是一个容器组件。它把UI组件和业务逻辑结合