React高级

  1. ReactDOM.createPortal(child, container):将子节点渲染到存在于父组件以外的 DOM 节点,即将组件插入到任意DOM节点;
    1. child:任何可渲染的 React 子元素,如一个元素、字符串、碎片;
    2. container:DOM元素;
    3. 通常讲,组件只能装配到最近的父元素上,而对于对话框、提示框等组件,需要跳出其容器;Portal 可以把组件放置到DOM树的任何地方。
  2. Context:一棵组件树的数据共享;
    1. 在父组件中设置数据,其任意层级的子节点都可以共享此数据,从而避免使用 props 层层向下传递数据;
    const {Provider, Consumer} = React.createContext(defaultValue);
    
    1. Provider 发布事件,Consumer 订阅事件,defaultValue 设置默认值;
    2. Consumer 将从最近的上层 Provider 读取数据。
  3. 错误边界:React 16引入的,用于捕获其子组件树 JavaScript 异常,记录错误并展示一个回退的UI(错误组件),而不是整个组件树的异常;
    1. componentDidCatch(error, info)error 表示被抛出的错误, info 表示一个含有 componentStack 属性的对象;
    2. try-catch 是命令式代码,错误边界 保留了React原生的声明特性。

Redux

  1. Redux:一个纯粹的状态管理系统,npm i redux -S

    1. redux是一个独立的专门用于做状态管理的JS库,并不是react的插件库;
    2. redux可以用在 React、Angular、Vue 等项目中,但通常与 React 配合使用;
    3. 功能类似于Vue中的 VueX,集中式管理组件之间的状态共享!
  2. 工作机制


    React高级_第1张图片
    222.jpg
  3. createStore():创建一个指定 reducerstore 对象;

  4. storeredux最核心的管理对象;

    1. 内部维护着 storereducer
    2. 核心方法:getState()dispatch(action)subscribe(listener)

核心概念

  1. action:标识要执行的行为对象,包括两个属性:type、xxx
    const action = {
        type: 'INCREMENT',    //标识属性,值为字符串,唯一,必要属性
        data: 2   //数据属性,任意名称和类型,可选
    }
    //创建```action```的工厂函数
    const increment = (number) => ({type:'INCREMENT', data: number})
    
  2. reducer:根据老的 stateaction,产生新的 state 的纯函数;
    export default function counter(state=0, action) {
        switch(action.type) {
            case 'INCREMENT':
                return state + action.data
            case 'DECREMENT':
                return state - action.data
            default:
                return state
        }
    }
    // 注意:函数返回一个新的状态,不要去直接修改原来的状态!
    
  3. store:将 stateactionreducer 联系在一起的对象;
    1. 创建 store 对象;
        import {createStore} from 'redux'
        import reducer from './reducers'
        const store = createStore(reducer);
    
    1. getState():获取 state
    2. dispatch(action):分发 action,触发 reducer 函数的调用,产生新的 state
    3. subscribe(listener):注册监听,当产生新的 state 时,自动调用。

基本使用

    npm install --save redux

创建目录 src/redux,用于存放 action-type.js、actions.js、reducers.js、store.js

  1. 创建 reducer 模块:reducers.js,包含 nreducer 函数,作用是根据老的stateaction,返回一个新的state
    export function counter(state=0, action) { //赋予state一个初始值,state是一个数值
        switch(action.type) {
            case 'INCREMENT':
                return state + action.data  //不要直接修改state,而是根据state的状态,返回一个新的state
            case 'DECREMENT':
                return state - action.data
            default:
                return state  //首次初始化调用时,返回的一个初始值
        }
    }
    
  2. action-type.js:存放常量字段
    export const INCREMENT = 'INCREMENT'
    export const DECREMENT = 'DECREMENT'
    
  3. actions.js:包含所有action creator
    import {INCREMENT, DECREMENT} from './action-type'
    export const increment = (number)=>({type: INCREMENT, data: number})
    export const decrement = (number)=>({type: DECREMENT, data: number})
    
  4. store.js:包含所有生成的 store 对象;
    import { createStore } from 'redux'
    import { counter } from './reducers'
    const store = createStore(counter);
    export default store  //如果创建了多个store,则使用 export 导出
    
  5. index.js 中导入 store.js
    import App from './components/app'
    import store from './redux/store'
    
    function render() {
        //把 store 传给App组件
        ReactDOM.render(, document.getElementById('root'))
    }
    
    //执行初始化渲染
    render();
    
    //监听更新状态的事件,重新渲染
    store.subscribe(render);
    
  6. App组件中导入 actions.js
    // actions.js中使用 export 多次导出的,要获取整个导出的对象,需要使用 * as xxx
    import * as actions from '../redux/actions'
    //获取到 store 对象的状态值
    const count = this.props.store.getState(); // 0
    //更新状态
    this.props.store.dispatch(actions.increment(10)); // 0+10
    this.props.store.dispatch(actions.decrement(2)); // 10-2
    

react-redux

react-reduxreact 的一个插件,降低 reduxreact 的代码耦合度;

    npm i react-redux --save

基本使用

  1. index.js
    import App from './components/app'
    import { Provider } from 'react-redux'
    import store from './redux/store'
    ReactDOM.render((
        
            
        
    ), document.getElementById('root'))
    
  2. App组件
    import { connect } from 'react-redux'
    import PropTypes from 'prop-types'
    import {increment, decrement} from '../redux/actions'
    class App extends Component {
        static propTypes = {  //声明接收的属性
            count: PropTypes.number.isRequired,
            increment: PropTypes.func.isRequired,
            decrement: PropTypes.func.isRequired,
        }
        //获取状态值
        const { count } = this.props;
        //调用更新状态的方法
        this.props.increment(21);  // 0+21
    }
    export default connect(
        state => ({count: state}),  //用count接收当前的状态值{count: state}
        {increment, decrement}  //更新状态的方法:{increment: increment, decrement: decrement}
    )(App)
    
    1. connect():连接组件与redux,传递给组件需要的参数;
    2. 参数一回调的当前状态,参数二传递更是状态的方法;
    3. {count: state}{increment, decrement}会被解构之后,传递给App组件:{count, increment, decrement}
    4. connect()返回一个函数,该函数的参数为一个组件,它会包装这个组件,并把count、increment、decrement传递给这个组件;
    
        
            ...
        
    
    
  3. 使用高阶组件的装饰器简化
    @connect(
        state => ({count: state}),  //用count接收当前的状态值{count: state}
        {increment, decrement}  //更新状态的方法:{increment: increment, decrement: decrement}
    )
    class App extends Component {
        static propTypes = {  //声明接收的属性
            count: PropTypes.number.isRequired,
            increment: PropTypes.func.isRequired,
            decrement: PropTypes.func.isRequired,
        }
        //获取状态值
        const { count } = this.props;
        //调用更新状态的方法
        this.props.increment(21);  // 0+21
    }
    export default App
    

组件拆分

  1. React-Redux将所有的组件分为两大类:UI组件、容器组件;
    1. UI组件:只负责UI的呈现,不带有任何业务逻辑;通过 props 接收数据,不使用任何Redux API,一般保存在components目录下;
    2. 容器组件:负责管理数据和业务逻辑,不负责UI的呈现,使用Redux API,一般保存在containers目录下;
  2. 组件App本身是UI组件,而 connect()Redux API,进行拆分
  3. 在src目录下创建两个目录:containers、components
  4. components/counter.jsx
    import PropTypes from 'prop-types'
    export default class Counter extends Component {
        static propTypes = {  //声明接收的属性
            count: PropTypes.number.isRequired,
            increment: PropTypes.func.isRequired,
            decrement: PropTypes.func.isRequired,
        }
        //获取状态值
        const { count } = this.props;
        //调用更新状态的方法
        this.props.increment(21);
    }
    
  5. containers/app.jsx
    import {connect} from 'react-redux'
    import {increment, decrement} from '../redux/actions'
    import Counter from '../components/counter'
    export default connect(
        state => ({count: state}),
        {increment, decrement}
    )(Counter)
    
  6. index.js
    import App from './containers/app'  //引入包装后的App组件
    import {Provider} from 'react-redux'
    import store from './redux/store'
    ReactDOM.render((
        
            
        
    ), document.getElementById('root'))
    

异步事件

  1. redux默认不支持异步处理,需要借助redux插件(异步中间件)
    npm i redux-thunk -S
    npm i redux-logger --save  //日志中间件
    
  2. store.jscreateStore() 支持第二个参数
    import {createStore, applyMiddleware} from 'redux'
    import thunk from 'redux-thunk'
    import logger from 'redux-logger'
    import {counter} from './reducers'
    //applyMiddleware() 应用中间件,thunk, logger 的顺序不能乱,自动打印日志
    const store = createStore(counter, applyMiddleware(thunk, logger));
    export default store
    
  3. actions.js:新增一个异步的action
    export const increment = (number)=>({type: INCREMENT, data: number})
    //异步的action返回一个函数
    export const incrementAsync = (number)=>{
        return dispatch => {
            //异步代码
            setTimeout(() => {
                dispatch(increment(number));
            }, 1000)  //1s之后去分发一个increment action
        }
    }
    
  4. 容器组件containers/app.jsx:加入incrementAsync
    import {increment, decrement, incrementAsync} from '../redux/actions'
    export default connect(
        state => ({count: state}),
        {increment, decrement, incrementAsync}
    )(Counter)
    
  5. 在组件 components/counter.jsx 中接收并调用异步action
    static propTypes = {  //声明接收的属性
        count: PropTypes.number.isRequired,
        increment: PropTypes.func.isRequired,
        decrement: PropTypes.func.isRequired,
        incrementAsync: PropTypes.func.isRequired,
    }
    //调用异步action
    this.props.incrementAsync(22);
    

redux的调试工具

  1. chrome浏览器:redux-devtools
  2. 依赖包:npm i redux-devtools-extension --save-dev
  3. 引入插件:store.js
    import {composeWithDevTools} from 'redux-devtools-extension'
    const store = createStore(counter, composeWithDevTools(applyMiddleware(thunk)));
    

多个reducer函数

  1. reducers.js:同时暴露多个reducer函数
    import { combineReducers } from 'redux'
    
    function counter(state=0, action) { //赋予state一个初始值,state是一个数值
        switch(action.type) {
            case 'INCREMENT':
                return state + action.data
            case 'DECREMENT':
                return state - action.data
            default:
                return state
        }
    }
    function comments(state=[], action) {  //state是一个数组
        switch(action.type) {
            case 'INCREMENT':  //新增一个数组元素
                return [action.data, ...state]  //不要直接修改state,而是根据state的状态,返回一个新的state
            case 'DECREMENT': //根据索引删除一个元素
                return state.filter((item, index) => index!==action.data) //filter() 不会修改原数组state
            default:
                return state  //首次初始化调用时,返回的一个初始值
        }
    }
    
    export default combineReducers({
        counter,
        comments
    })
    
  2. combineReducers()让redux向外暴露的state是一个对象结构:{ counter: 0, comments: [] }
  3. store.js
    import reducers from './reducers'
    
    const store = createStore(reducers, applyMiddleware(thunk));
    
    export default store
    
  4. 容器组件app.jsx:获取state时,需要指定当前的state
        export default connect(
            state => ({count: state.counter}),
            {increment, decrement, incrementAsync}
        )(Counter)
    

你可能感兴趣的:(React高级)