• 使用RXJS实现React中的redux功能


    起因

    • 偶然间看了一下Angular的服务的依赖注入和rxjs的观察者流的使用,觉得还有点意思,就想在React中实现一下类似的。

    准备工作

    • 一个空的React项目。
    • 安装rxjsnpm install rxjs --save;

    实现思路

    1. 首先是根据业务需求,将需要抽离的全局状态使用不同的服务类进行包裹,进行数据分离。
    2. 实现一个自定的Providerconnect方法,将对应的全局状态和服务进行组件的注入。
    3. 在组件中编写mapStateToProps自定义需要获取的服务以及全局状态。

    代码实现

    // 用来包裹服务类,生成一个实例存到闭包中,保证每次拿到的服务对象是同一个实例
    const wrapService = (Service, initData = {}) => {
      const service = new Service(initData);
      return () => {
        return service;
      }
    }
    
    // 一个服务类
    import { Subject } from 'rxjs';
    import { wrapService } from 'Utils/service';
    
    class TestService {
      constructor(initData = {}) {
        this.data = initData;
      }
    
      data$ = new Subject();
    
      subscribe = (callback) => {
        this.subscription = this.data$.subscribe(callback);
        this.data$.next(this.data);
    
        return () => {
          this.subscription.unsubscribe();
        }
      }
    
      add = (data) => {
        this.data.count = Number(data.count) + Number(this.data.count);
        this.data$.next(this.data);
      }
    
      set = (data) => {
        this.data = { ...this.data, ...data };
        this.data$.next(this.data);
      }
    }
    
    const initData = {
      count: 0,
      test1: 'Hello',
      test2: 'World'
    };
    
    export default wrapService(TestService, initData)();
    
    // 获取唯一的context
    const getContext = (() => {
      const Context = React.createContext(null);
      Context.displayName = 'serviceProvider';
    
      return () => {
        return Context;
      }
    })();
    
    // 默认注入所有的全局状态
    const defaultMapStateToProps = (data) => {
      return {
        ...data
      };
    }
    
    /**
     * 注入服务的高阶组件
     */
    function ServiceProvider({ services = {}, children = null }) {
      const Context = getContext();
      const [contextValue, setContextValue] = useState({
        ...services
      });
      const unsubscribeList = [];
    
      useEffect(() => {
        Object.keys(services).forEach((key) => {
          const unsubscribe = services[key].subscribe(_data => {
            setContextValue({
              ...contextValue,
              ..._data
            });
          });
          unsubscribeList.push(unsubscribe);
        });
    
        return () => {
          unsubscribeList.forEach(unsubscribe => {
            typeof unsubscribe === 'function' && unsubscribe();
          });
        }
      }, [])
    
      return (
        <Context.Provider value={contextValue}>{children}</Context.Provider>
      )
    }
    
    // 向组件中注入全局的状态和服务对象
    const inject = (mapStateToProps = defaultMapStateToProps) => {
      const Context = getContext();
    
      return (Component) => (props) => {
    
        return (
          <Context.Consumer>
            {
              data => {
                const newProps = mapStateToProps(data);
                return <Component {...props} {...newProps} />
              }
            }
          </Context.Consumer>
        )
      }
    }
    
    // 使用,先在最外层的render中使用Provider包裹。
       ReactDOM.render(
        <ServiceProvider services={{ testService: testService }}>
          <Index />
        </ServiceProvider>
        ,
        document.getElementById("root")
    // 然后在需要的地方,使用inject方法对组件进行包裹。
    const mapStateToProps = (data) => {
      const {
        count,
        testService
      } = data;
    
      return {
        count,
        testService
      }
    }
    
    export default inject(mapStateToProps)(Subscribe);
    
    // 这样就在Subscribe组件中引入了对应的全局状态count和服务testService,直接在props中可以拿到。
    

    小结

  • 相关阅读:
    SQL Server XML数据解析
    c# XML和实体类之间相互转换(序列化和反序列化)
    C#解析XML详解(XPath以及带命名空间NameSpace)
    Jquery实现按钮点击遮罩加载,处理完后恢复
    jquery控制div随滚动条滚动效果
    asp.net中利用Jquery+Ajax+Json实现无刷新分页(二)
    easyUI tree点击文字展开节点
    201805牛客模拟考
    策略模式
    非线程安全演变成线程安全---原子性与加锁机制
  • 原文地址:https://www.cnblogs.com/aloneMing/p/13294511.html
Copyright © 2020-2023  润新知