Last Updated 2020-04-28 16:45:94

初体验

以下代码片段将展示 concent slogan 里的特性,方便你快速了解它与其它状态管理的巨大差异。

0 入侵

使用 concent,可以在你原始 react 组件一行代码不改动的情况下,直接接入状态管理。

❤️0入侵

启动concent,载入模块配置

import { run } from 'concent';
run({
  counter: {
    //定义counter模块
    state: { count: 1 }, //state定义,必需
  },
});

定义类组件

import { register } from 'concent';
//注册成为Concent Class组件,指定其属于counter模块
@register('counter')
class CounterComp extends Component {
  render() {
    const { count } = this.state;
    return (
      <div>
        count: {count}
        <button onClick={() => this.setState({ count: count + 1 })}>inc</button>
        <button onClick={() => this.setState({ count: count - 1 })}>dec</button>
      </div>
    );
  }
}

代码0改动接入concent



渐进式

得益于 concent 灵活的 api 设计,允许你逐步的解耦渲染逻辑与业务逻辑,让组件保持只输出视图的最小职责,彻底提高代码的可阅读性与可维护性。

🌟渐进式

新增reducer定义

import { run } from 'concent';
run({
  counter: {
    //定义counter模块
    state: { count: 1 }, //state定义,必需
    reducer: {
      //reducer函数定义,可选
      inc(payload, moduleState) {
        return { count: moduleState.count + 1 };
      },
      dec(payload, moduleState) {
        return { count: moduleState.count - 1 };
      },
    },
  },
});

通过dispatch修改状态

import { register } from 'concent';
//注册成为Concent Class组件,指定其属于counter模块
@register('counter')
class CounterComp extends Component {
  render() {
    //ctx是concent为所有组件注入的上下文对象,携带为react组件提供的各种新特性api
    return (
      <div>
        count: {this.state.count}
        <button onClick={() => this.ctx.dispatch('inc')}>inc</button>
        <button onClick={() => this.ctx.dispatch('dec')}>dec</button>
      </div>
    );
  }
}

逐步解耦业务逻辑与ui渲染



对于 concent 来说,Hoc class, renderProps, hook 三种组件写法是高度统一的,对于 concent 来说,它们只是渲染的载体,注入的实例上下文对象ctx才是 concent 的灵魂,让你可以在它们 3 种形态之间丝滑的任意切换,所以你大可以不用担心 class 组件与 function 组件怎么共享代码以及怎么共享业务逻辑。

定义为RenderProps组件

import { registerDumb } from 'concent';
const CounterRenderPropsComp = registerDumb('counter')((ctx) => {
  return (
    <div>
      count: {ctx.state.count}
      <button onClick={() => ctx.dispatch('inc')}>inc</button>
      <button onClick={() => ctx.dispatch('dec')}>dec</button>
    </div>
  );
});

定义为Hook组件

import { useConcent } from 'concent';
function CounterHookComp() {
  const ctx = useConcent('counter');
  return (
    <div>
      count: {ctx.state.count}
      <button onClick={() => ctx.dispatch('inc')}>inc</button>
      <button onClick={() => ctx.dispatch('dec')}>dec</button>
    </div>
  );
}

高性能

基于依赖标记引用收集状态分发原理工作,内置renderKeylazyDispatchdelayBroadcast等特性,从状态提交那一刻,concent 就精确的知道怎么样缩小渲染范围、减少渲染次数、降低渲染频率,保证大型 react 工程的极致的渲染效率。

⚡️高性能

renderKey

缩小长列表渲染范围



点我查看视频源码

lazyDispatch

减小渲染次数



点我查看视频源码

delayBroadcast

降低渲染频率



点我查看视频源码

增强 react

有了实例上线文ctx,配合setup特性,concent 可以非常从容自然的增强 react 组件功能,离开这些功能你依然能够开发 react 应用,但用上这些功能能你拥抱更好的开发范式,全面提升大型 react 工程的编码优化度和架构体验。
setup是针对组件实例提供的一个非常重要的特性,在类组件和函数组件里都能够被使用,它会在组件首次渲染之前会被触发执行一次,其返回结果收集在ctx.settings里,之后便不会再被执行,所以可以在其中定义实例computed实例watch实例effect等钩子函数,同时也可以自定义其他的业务逻辑函数并返回,方便组件使用。 利用 setup 只执行一次的特性,可以让函数组件省去重复渲染期间,重复生成临时闭包函数,同时需要手动调用useCallback等辅助优化函数

🛠增强react

实例computed、watch、effect、emit&on etc...

import { run, useConcent } from 'concent';

run({
  counter: {
    //定义counter模块
    state: {
      //【必需】定义state
      count: 1,
      msg: '',
    },
    reducer: {
      //【可选】定义reducer,解耦业务逻辑与ui渲染
      inc(num, moduleState, actionCtx) {
        return { count: moduleState.count + num };
      },
      changeMsg(msg, moduleState, actionCtx) {
        return { msg };
      },
      async complexInc(num, moduleState, actionCtx) {
        await api.track('inc');
        actionCtx.dispatch('inc', inc);
        actionCtx.dispatch('changeMsg', 'call complexInc');
      },
    },
    computed: {
      //【可选】定义模块计算函数
      count: (count) => count * 2,
      // 收集到此函数的依赖为 ['count', 'msg']
      autoDep: (newState, oldState, fnCtx) => {
        const { count, msg } = newState;
        const { changed } = fnCtx;
        return `${changed.join(',')} changed, count ${count}, msg ${msg}`;
      },
      manualDep: {
        fn: (newState, oldState, fnCtx) => {
          const { changed } = fnCtx;
          return `${changed.join(',')} changed`;
        },
        // 人工标记依赖为 ['count', 'msg'],不管函数体有没有用到这些值,只要它们改变了就触发此计算函数
        depKeys: ['count', 'msg'],
      },
    },
    watch: {
      //【可选】定义模块观察函数
      count: (count, oldVal) => console.log(`old ${oldVal} new ${count}`),
      anyOneChange: {
        fn: (newState, oldState, fnCtx) => {
          console.log(`${changed.join(',')} changed`);
        },
        depKeys: ['count', 'msg'],
      },
    },
    init: async () => {
      //【可选】异步的初始化模块状态
      const data = await api.fetchData();
      return data;
    },
  },
});

const setup = (ctx) => {
  //count变化时的副作用函数,第二位参数可以传递多个值,表示任意一个发生变化都将触发此副作用
  ctx.effect(() => {
    console.log('count changed');
  }, ['count']);
  //每一轮渲染都会执行
  ctx.effect(() => {
    console.log('trigger every render');
  });
  //仅首次渲染执行的副作用函数
  ctx.effect(() => {
    console.log('trigger only first render');
  }, []);

  //定义实例computed,因每个实例都可能会触发,优先考虑模块computed
  ctx.computed('count', ({ count }, oldState, fnCtx) => {
    return count * 2;
  });

  //定义实例watch,区别于effect,执行时机是在组件渲染之前
  //因每个实例都可能会触发,优先考虑模块watch
  ctx.watch('count', ({ count }, oldState, fnCtx) => {
    //发射事件
    ctx.emit('countChanged', newVal);
    api.track(`count changed to ${newVal}`);
  });

  //定义事件监听,concent会在实例销毁后自动将其off掉
  ctx.on('changeCount', (count) => {
    ctx.setState({ count });
  });

  return {
    inc: () => ctx.dispatch('inc', ctx.state.count + 1),
    complexInc: () => ctx.dispatch('complexInc', ctx.state.count + 1),
  };
};

const iState = { privCount: 0 }; //此state相当于组件的私有状态
function HookFnComp() {
  const {
    state: { privCount, count, msg },
    settings: { inc, dec, incStoreCount, decStoreCount },
  } = useConcent({ module: 'counter', setup, state: iState });

  return (
    <div>
      count: {count}
      msg: {msg}
      privCount: {privCount}
      <button onClick={inc}>+</button>
      <button onClick={complexInc}>+</button>
    </div>
  );
}

拥抱更好的开发范式



点我查看视频源码