# redux进阶

# UI组件和容器组件

  • 有些时候,当我们把一个组件的逻辑和渲染都放到同一个组件去管理时,这个组件的维护就会显得比较困难,所以,我们可以对这个组件做拆分
  • UI组件只负责页面渲染,傻瓜组件,没有任何的逻辑处理
  • 容器组件只负责页面逻辑处理,聪明组件,只负责组件的业务逻辑,功能实现,不负责任何的UI渲染

# 无状态组件

  • 当一个普通的组件中只有render函数时,建议可以用一个无状态组件来替换它,无状态组件,其实就是一个函数,它的性能是最优的
  • 无状态组件,相对于普通组件的优势:
    • 性能比较高,因为它就是一个函数,而普通组件,它是js中的一个类,这个类生成的对象中,还会有生命周期函数,所以它在执行时,既要执行生命周期函数,又要执行render函数,远比一个函数要执行的东西要多得多
  • 一般UI组件都可以用无状态组件来优化,但也不是绝对的,有时UI组件也会负责一些简单的业务逻辑,是否改写要视情况而定

# Redux中发送异步请求获取数据

  • 使用axios发送异步请求

# 使用Redux-thunk中间件实现ajax数据请求

  • 通过redux创建store的时候,才会使用中间件,这个中间件是redux的中间件,而非react的
  • redux-thunk, redux-devtools-extentions都是redux的中间件
  • 使用redux-thunk后,actionCreator返回的就不仅可以是一个对象,还可以是一个函数,该函数接收两个参数,dispatch和getState,他们都是store的方法
// 查询指南
import { createStore, applyMiddleware, compose } from 'redux'
import reducer from './reducer'
import thunk from 'redux-thunk'

const composeEnhancers =
  typeof window === 'object' &&
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?   
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}) : compose;

const enhancer = composeEnhancers(
  applyMiddleware(thunk)
);

const store = createStore(reducer, enhancer);
export default store
  • 使用方法
    • 安装redux-thunk npm i redux-thunk --save
    • 依照官方文档配置store的index.js,使react既可以使用react-thunk中间件,又可以使用redux-devtools-extentions这个开发者工具
    • 配置好redux-thunk环境后,我们就可以在action中写异步的代码,即action不仅可以是一个JS对象,还可以是一个函数,在这个函数中,我们就可以执行异步操作
    • store.dispatch(action)时,发现action不是一个对象,而是一个函数,就会帮我们自动执行一下这个函数
    • 随着代码量的增多,把异步操作都放到生命周期函数中来,会使代码变得很复杂,不好管理,所以应该把复杂的业务逻辑和异步逻辑拆分到一个地方去管理
    • 现在借助redux-thunk,我们就可以把这些复杂的业务逻辑包括异步请求放到createAction中去管理,而且还方便作自动化测试
  • redux-thunk中间件的底层原理
    • redux是action和store之间的一个中间件
    • 在redux中action只能是一个对象,使用redux-thunk之后,action可以是一个函数了
    • redux-thunk中间件其实是对store的dispatch的方法的一个封装和升级,ta会根据参数的不同,执行不同的事情,如果参数是一个对象,ta会直接传递给store,如果参数是一个函数,ta会先执行一下这个函数
  • redux的其他常用中间件
    • redux-logger 可以记录action每次派发的日志,原理:每次在传递action之前,通过console.log把这个action打印出来,这样就可以在每次派发action之前,把这个action的信息打印出来
    • redux-saga 也是解决react中异步问题的一个中间件,不同与redux-thunk,redux-thunk是把异步操作放到action中去操作,redux-saga则是单独的把异步逻辑拆分出来,放到另一个文件中去管理

# redux-saga中间件的使用 ??

  • 中间指的是action和store的中间,所以它指的是redux的中间件,而不是react的,只有redux中才有action和store 的概念

# React-Redux的使用

  • React-Redux是一个第三方模块,ta可以帮助我们更加方便的使用Redux
  • Provider 第一个核心api,提供器,作用:关联store, 使其下所有的子组件都有能力获取到store中的数据
npm install react-redux --save
import { Provider } from 'react-redux'
import store from './store'

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

ReactDOM.render(App, document.getElementById('root'))
  • connect 第二个核心api,连接器,作用:connect使TodoList组件与store,以一定的映射关系做连接
  • mapStateToProps和mapDispatchToProps是两个映射规则,一个是数据,一个是改变数据的方法
import { connect } from 'react-redux'
// mapStateToProps将store中的数据与组件Props下的数据做映射,
// 接收state对象做参数,返回一个对象
const mapStateToProps = (state) =>{
  return {
    inputValue: state.inputValue,
    list: state.list
  }
}
// mapDispatchToProps将store.dispatch()方法与组件Props下的方法做映射,
// 接收dispatch()方法做参数,返回一个对象
const mapDispatchToProps = (dispatch) => {
  return {
    handleInputValue(e){
      const action = {
        type: 'handle_input_value',
        value: e.target.value
      }
      dispatch(action)
    },
    handleBtnClick(){
      const action = {
        type: 'handle_btn_click'
      }
      dispatch(action)
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(TodoList)
  • TodoList是一个UI组件,当使用connect把一些数据和业务逻辑与TodoList相结合时,返回的内容其实就是一个容器组件

# 手写简版React-Redux

import React, { useState, useContext, useEffect } from "react";
// import { bindActionCreators } from "redux";

const Context = React.createContext();

export function Provider({ store, children }) {
  return <Context.Provider value={store}>{children}</Context.Provider>;
}

export const connect = (
  mapStateToProps = state => state,
  mapDispatchToProps = {},
) => Cmp => props => {
  const store = useContext(Context);
  const getMoreProps = () => {
    console.log(store.getState())
    const stateProps = mapStateToProps(store.getState());
    const dispatchProps = bindActionCreators(
      mapDispatchToProps,
      store.dispatch,
    );
    return {
      ...stateProps,
      ...dispatchProps,
    };
  };
  useEffect(() => {
    store.subscribe(() => {
      setMoreProps({ ...moreProps, ...getMoreProps() });
    });
  }, []);
  const [moreProps, setMoreProps] = useState(getMoreProps());
  return <Cmp {...props} {...moreProps} />;
};

function bindActionCreator(creator, dispatch) {
  return (...args) => dispatch(creator(...args));
}
//{add: ()=>({type:'add'}), minus: ()=>({type: 'minus})
function bindActionCreators(actionCreators, dispatch) {
  let obj = {};
  for (let key in actionCreators) {
    obj[key] = bindActionCreator(actionCreators[key], dispatch);
  }
  return obj;
}