0.0.1 • Published 10 months ago

react-easybus v0.0.1

Weekly downloads
-
License
MIT
Repository
-
Last release
10 months ago

react-easybus

简洁、高效的实现 react 跨模块通信,数据共享。

特点

  • ✔ 设计简单
  • ✔ 功能完整
  • ✔ 性能强劲
  • ✔ 解耦通信
  • ✔ 开箱即用
  • ✔ 函数组件 hooks 最强搭档
  • ✔ 完美支持 typescript 类型检测

安装

npm install react-easybus

场景

  • 页面级组件通信(PageBus)
  • 应用级组件通信(AppBus)
  • 可复用组件通信(ComponentBus)

页面级组件通信(PageBus)

单页面应用一般是通过切换路由的方式调用 JS 重新渲染 DOM 的方式实现的页面切换,一个页面的功能会有很多组件组成,这些组件通常会有一定的数据通信,比如父组件通过 props 把值传递给子组件,子组件调用一个方法更新父组件的状态,兄弟组件之间的通信等。

跨模块状态使用及更新(代替 props)

  • $bus.useState(~) 返回依赖状态
  • $bus.setState(~) 更新状态

通过以上 2 个 api 即可实现跨任意层级的模块状态的使用和更新

声明通信桥对象$bus.ts

import { PageBus } from 'react-easybus';
const $bus = PageBus<{
  name: string;
}>.create();
export default $bus;

页面入口模块Page.tsx

import React from 'react';
import Mod1 from './Mod1';
import Mod2 from './Mod2';
import $bus from './$bus';
function Page() {
  // 在入口模块安装并初始化状态
  $bus.initState({
    name: 'name',
  });

  return (
    <>
      <Mod1 />
      <Mod2 />
    </>
  );
}

子模块Mod1.tsx

import $bus from './$bus';
export default function Mod1() {
  // 从$bus中引入name状态
  const { name } = $bus.useState(['name']);
  return <div>名称是:{name}</div>;
}

子模块Mod2.tsx

import Mod3 from './Mod3';
export default function Mod2() {
  return <Mod3 />;
}

孙子模块Mod3.tsx

import $bus from './$bus';
export default function Mod3() {
  // 从$bus中引入name状态,也可以使用 const { name } = $bus.resState; 来导入并监听状态
  const { name } = $bus.useState(['name']);

  return (
    <>
      <div>名称是:{name}</div>
      <button
        onClick={() => {
          // 更新name状态,Mod1,Mod3都会更新
          $bus.setState({
            name: 'newName',
          });
        }}
      >
        更新名称
      </button>
    </>
  );
}

跨模块通信 1

声明通信桥对象$bus.ts

import { PageBus } from 'react-easybus';
const $bus = PageBus.create(
  class MyBus extends PageBus {
    setName: (name: string) => void;
  }
);
export default $bus;

入口模块 Page.tsx

import React from 'react';
import Mod1 from './Mod1';
import Mod2 from './Mod2';
import $bus from './$bus';
function Page() {
  $bus.initState();

  return (
    <>
      <Mod1 />
      <Mod2 />
    </>
  );
}

子模块Mod1.tsx

import $bus from './$bus';
export default function Mod1() {
  const [value, setValue] = useState('');

  $bus.setName = (value) => setValue(value);

  return <div>Mod2输入的值是:{value}</div>;
}

子模块Mod2.tsx

import $bus from './$bus';
export default function Mod2() {
  return (
    <div>
      <input
        onChange={(e) => {
          $bus.setName(e.target.value);
        }}
      />
    </div>
  );
}

跨模块通信 2

  • $bus.useMessager(cmd, callback) 监听消息
  • $bus.callMessager(cmd, payload) 广播消息

通过以上 2 个 api 实现无耦合的跨模块通信

声明通信桥对象$bus.ts

import { createBridge, PageBus } from 'react-easybus';
const $bus = createBridge(
  PageBus<{
    setName: {
      name: string;
    };
  }>
);
export default $bus;

入口模块 Page.tsx

import React from 'react';
import Mod1 from './Mod1';
import Mod2 from './Mod2';
import $bus from './$bus';
function Page() {
  $bus.initState();

  return (
    <>
      <Mod1 />
      <Mod2 />
    </>
  );
}

子模块Mod1.tsx

import $bus from './$bus';
export default function Mod1() {
  const [value, setValue] = useState('');

  $bus.useMessager('setName', (payload) => {
    setValue(payload.name);
  });

  return <div>Mod2输入的值是:{value}</div>;
}

子模块Mod2.tsx

import $bus from './$bus';
export default function Mod2() {
  return (
    <div>
      <input
        onChange={(e) => {
          $bus.callMessager('setName', {
            name: e.target.value,
          });
        }}
      />
    </div>
  );
}

一个完整的示例

PageBus(页面级状态管理)

$bus.ts (桥对象声明)

import { PageBus } from 'react-easybus';

// 签名可以省略,加了之后可以享受ts的各种好处
const $bus = PageBus.create(
  class MyPageBus extends PageBus<
    // 状态签名
    {
      name: string;
      age: number;
    },
    // 消息签名
    {
      hello: {
        message: string;
      };
    }
  > {
    // 方法签名
    toggleTips: () => void;
  }
);

export default $bus;

page.tsx 页面使用

import React, { memo, useState } from 'react';
import $bus from './$bus';

// 入口模块
export default memo(() => {
  $bus.initState({
    name: '张三',
    age: 18,
  });

  return (
    <div>
      <Mod1 />
      <Mod2 />
      <Mod3 />
      <Mod4 />
      <Mod5 />
    </div>
  );
});

// 状态使用
const Mod1 = memo(() => {
  const { age } = $bus.resState;

  return <div>年龄:{age}</div>;
});

// 状态更新
const Mod2 = memo(() => {
  return (
    <div
      onClick={() => {
        // 直接调用update更新桥状态
        $bus.setState((state) => {
          state.age++;
        });
      }}
    >
      更新年龄
    </div>
  );
});

// 方法挂载
const Mod3 = memo(() => {
  const [visible, setVisible] = useState(false);

  // 挂载方法,其他模块可以通过$bus.toggleTips()完成跨模块通信
  $bus.toggleTips = () => {
    setVisible(!visible);
  };

  return <div style={{ display: visible ? '' : 'none' }}>tipsBox</div>;
});

// 方法调用
const Mod4 = memo(() => {
  return (
    <div>
      <button
        onClick={() => {
          $bus.toggleTips();
        }}
      >
        切换显示tips
      </button>
    </div>
  );
});

// 消息监听
const Mod5 = memo(() => {
  const [message, setMessage] = useState('');

  $bus.useMessager('hello', (payload) => {
    setMessage(payload.message);
  });

  return <div>接收的消息为:{message}</div>;
});

// 消息广播
const Mod6 = memo(() => {
  return (
    <div>
      <button
        onClick={() => {
          $bus.callMessager('hello', {
            message: String(Math.random()),
          });
        }}
      >
        广播消息
      </button>
    </div>
  );
});

API

  • PageBus.create(MyPageBus) 创建局部桥对象
  • $bus.initState(initalState:State) 安装并初始化桥对象状态
  • $bus.useState(option:Array<keyof State> | (state:State) => any) 监听并返回状态中的值,当 option 为数组时,则返回状态在数组中的对应字段,假如 option 为函数,则使用函数返回值作为监听的数据
  • $bus.setState(nextState: State | (state)=>state ) 更新桥状态,假如是对象,则和原状态进行浅合并,假如是函数,则使用返回值替换原状态,假如函数无返回值,则可以通过直接操作 state 进行更新状态
  • $bus.xxx = (...args) => any 挂载方法,允许其他模块通过$bus.xxx(...args)的方式与当前模块进行通信
  • $bus.useMessager(cmd, callback, delay?:number); $bus.callMessager(cmd, payload) 创建消息通信,允许不同模块创建消息监听,可以直接接受任何模块的广播消息并做出响应,广播消息允许发送参数payload,假如存在delay,那么广播的消息在相应的时间内进行合并处理

$bus.useState(~) 示例,通过 useState 进行状态精确监听,优化渲染性能

/// state = {name:'张三',age:18,info:{address:'浙江省'}}

// 方式1:返回name,age并且state.name和state.age发生变更时,都会触发函数render
const { name, age } = $bus.useState(['name', 'age']);

// 方式2:返回并监听state.info.address,仅在address的值发生变化时才触发函数render
const { address } = $bus.useState((state) => ({
  address: state.info.address,
}));

AppBus(全局级状态管理)

全局应用状态管理会跨页面进行通信,主要用于一些全局状态比如用户信息,应用配置等的数据管理

$bus.ts 声明桥对象

import { AppBus } from 'react-easybus';

const $bus = AppBus.create(
  class MyAppBus extends AppBus<
    // 状态签名
    {
      name: string;
      age: number;
    },
    // 消息签名
    {
      hello: {
        message: string;
      };
    }
  > {
    toggleTips() {
      // ...
    }
  },
  { name: '张三', age: 18 }
);

export default $bus;

全局桥对象不需要$bus.initState(~),初始化状态在 create 的时候作为第二个参数传入,其他接口和PageBus完全保持一致

完美结合 hooks

通过挂载Dispatcher或者监听广播Messager的方式,可以完美的与useState结合进行模块状态控制,通信调用,消息模式可以解耦模块逻辑,不会出现调用不存在的方法导致脚本错误

结合 typescript

通过State,Dispatcher,Messager定义签名,完全满足 typescript 的类型检测和自动提示

说明

更多示例见 node_modules/react-easybus/sample 使用模板见 node_modules/react-easybus/template

完整 API

  • $bus.useState(~) 创建状态依赖并返回状态值
  • $bus.setState(~) 更新状态
  • $bus.xxx(~) 调用 dispatcher 方法,以继承的方式定义
  • $bus.useMessager(~) 创建消息监听
  • $bus.callMessager(~) 广播消息
  • PageBus 页面桥类
    • PageBus.create(~)
    • $bus.initState(~) 安装桥对象
  • AppBus 应用桥类
    • AppBus.create(~)
    • AppBusProvider 全局桥上下文
  • ComponentBus 组件桥类
    • ComponentBus.create(~)
    • initState(~) 安装桥对象
    • useBus(~) 使用桥对象
0.0.1

10 months ago