使用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中可以拿到。

小结

使用RXJS实现React中的redux功能

上一篇:2017年Brats 脑肿瘤挑战赛Automatic Brain Tumor Segmentation using Cascaded Anisotropic Convolutional Neural Networks


下一篇:html2canvas使用中的小问题