npm package: @react-fox/bridge
Description: react 状态管理
Author: sschen86@163.com
Latest version: 0.1.1

# @react-fox/bridge

简洁、高效的实现 react 跨模块通信，数据共享。

## 特点

- ✔ 设计简单
- ✔ 功能完整
- ✔ 性能强劲
- ✔ 解耦通信
- ✔ 开箱即用
- ✔ 函数组件 hooks 最强搭档
- ✔ 完美支持 typescript 类型检测

## 安装

```bash
npm install @react-fox/bridge
```

## 场景

- 页面级组件通信（LocalBridge）
- 应用级组件通信（GlobalBridge）
- 可复用组件通信（WidgetBridge）

## 页面级组件通信（LocalBridge）

单页面应用一般是通过切换路由的方式调用 JS 重新渲染 DOM 的方式实现的页面切换，一个页面的功能会有很多组件组成，这些组件通常会有一定的数据通信，比如父组件通过 props 把值传递给子组件，子组件调用一个方法更新父组件的状态，兄弟组件之间的通信等。

### 跨模块状态使用及更新（代替 props）

- $bridge.connect(~) 返回依赖状态
- $bridge.update(~) 更新状态

通过以上 2 个 api 即可实现跨任意层级的模块状态的使用和更新

#### 声明通信桥对象`$bridge.ts`

```tsx
import { createBridge, LocalBridge } from '@shushu.pro/bridge';
const $bridge = createBridge(
  LocalBridge<{
    name: string;
  }>
);
export default $bridge;
```

#### 页面入口模块`Page.tsx`

```tsx
import React from 'react';
import Mod1 from './Mod1';
import Mod2 from './Mod2';
import $bridge from './$bridge';
function Page() {
  // 在入口模块安装$bridge并初始化状态
  $bridge.install({
    name: 'name',
  });

  return (
    <>
      <Mod1 />
      <Mod2 />
    </>
  );
}
```

#### 子模块`Mod1.tsx`

```tsx
import $bridge from './$bridge';
export default function Mod1() {
  // 从$bridge中引入name状态
  const { name } = $bridge.connect(['name']);
  return <div>名称是：{name}</div>;
}
```

#### 子模块`Mod2.tsx`

```tsx
import Mod3 from './Mod3';
export default function Mod2() {
  return <Mod3 />;
}
```

#### 孙子模块`Mod3.tsx`

```tsx
import $bridge from './$bridge';
export default function Mod3() {
  // 从$bridge中引入name状态
  const { name } = $bridge.connect(['name']);
  return (
    <>
      <div>名称是：{name}</div>
      <button
        onClick={() => {
          // 更新name状态，Mod1,Mod3都会更新
          $bridge.update({
            name: 'newName',
          });
        }}
      >
        更新名称
      </button>
    </>
  );
}
```

### 跨模块通信 1

#### 声明通信桥对象`$bridge.ts`

```tsx
import { createBridge, LocalBridge } from '@shushu.pro/bridge';
const $bridge = createBridge(
  class Bridge extends LocalBridge {
    setName: (name: string) => void;
  }
);
export default $bridge;
```

#### 入口模块 `Page.tsx`

```tsx
import React from 'react';
import Mod1 from './Mod1';
import Mod2 from './Mod2';
import $bridge from './$bridge';
function Page() {
  // 安装并初始化状态
  $bridge.install();

  return (
    <>
      <Mod1 />
      <Mod2 />
    </>
  );
}
```

#### 子模块`Mod1.tsx`

```tsx
import $bridge from './$bridge';
export default function Mod1() {
  const [value, setValue] = useState('');

  $bridge.setName = (value) => setValue(value);

  return <div>Mod2输入的值是：{value}</div>;
}
```

#### 子模块`Mod2.tsx`

```tsx
import $bridge from './$bridge';
export default function Mod2() {
  return (
    <div>
      <input
        onChange={(e) => {
          $bridge.setName(e.target.value);
        }}
      />
    </div>
  );
}
```

### 跨模块通信 2

- $bridge.onMessage(cmd, callback) 监听消息
- $bridge.postMessage(cmd, payload) 广播消息

通过以上 2 个 api 实现无耦合的跨模块通信

#### 声明通信桥对象`$bridge.ts`

```tsx
import { createBridge, LocalBridge } from '@shushu.pro/bridge';
const $bridge = createBridge(
  LocalBridge<{
    setName: {
      name: string;
    };
  }>
);
export default $bridge;
```

#### 入口模块 `Page.tsx`

```tsx
import React from 'react';
import Mod1 from './Mod1';
import Mod2 from './Mod2';
import $bridge from './$bridge';
function Page() {
  // 安装并初始化状态
  $bridge.install();

  return (
    <>
      <Mod1 />
      <Mod2 />
    </>
  );
}
```

#### 子模块`Mod1.tsx`

```tsx
import $bridge from './$bridge';
export default function Mod1() {
  const [value, setValue] = useState('');

  $bridge.onMessage('setName', (payload) => {
    setValue(payload.name);
  });

  return <div>Mod2输入的值是：{value}</div>;
}
```

#### 子模块`Mod2.tsx`

```tsx
import $bridge from './$bridge';
export default function Mod2() {
  return (
    <div>
      <input
        onChange={(e) => {
          $bridge.postMessage('setName', {
            name: e.target.value,
          });
        }}
      />
    </div>
  );
}
```

## 一个完整的示例

### LocalBridge（页面级状态管理）

#### $bridge.ts (桥对象声明)

```ts
import { createBridge, LocalBridge } from '@react-fox/bridge';

// 签名可以省略，加了之后可以享受ts的各种好处
const $bridge = createBridge(
  class MyBridge extends LocalBridge<
    // 状态签名
    {
      name: string;
      age: number;
    },
    // 消息签名
    {
      hello: {
        message: string;
      };
    }
  > {
    // 方法签名
    toggleTips: () => void;
  }
);

export default $bridge;
```

#### page.tsx 页面使用

```tsx
import React, { memo, useState } from 'react';
import $bridge from './$bridge';

// 入口模块
export default memo(() => {
  // 安装并初始化状态
  $bridge.install({
    name: '张三',
    age: 18,
  });

  return (
    <div>
      <Mod1 />
      <Mod2 />
      <Mod3 />
      <Mod4 />
      <Mod5 />
    </div>
  );
});

// 状态使用
const Mod1 = memo(() => {
  // 监听并返回age字段，所有的state下的字段都可以通过connect进来
  const { age } = $bridge.connect(['age']);

  return <div>年龄：{age}</div>;
});

// 状态更新
const Mod2 = memo(() => {
  return (
    <div
      onClick={() => {
        // 直接调用update更新桥状态
        $bridge.update((state) => {
          state.age++;
        });
      }}
    >
      更新年龄
    </div>
  );
});

// 方法挂载
const Mod3 = memo(() => {
  const [visible, setVisible] = useState(false);

  // 挂载方法，其他模块可以通过$bridge.toggleTips()完成跨模块通信
  $bridge.toggleTips = () => {
    setVisible(!visible);
  };

  return <div style={{ display: visible ? '' : 'none' }}>tipsBox</div>;
});

// 方法调用
const Mod4 = memo(() => {
  return (
    <div>
      <button
        onClick={() => {
          $bridge.toggleTips();
        }}
      >
        切换显示tips
      </button>
    </div>
  );
});

// 消息监听
const Mod5 = memo(() => {
  const [message, setMessage] = useState('');

  $bridge.onMessage('hello', (payload) => {
    setMessage(payload.message);
  });

  return <div>接收的消息为：{message}</div>;
});

// 消息广播
const Mod6 = memo(() => {
  return (
    <div>
      <button
        onClick={() => {
          $bridge.postMessage('hello', {
            message: String(Math.random()),
          });
        }}
      >
        广播消息
      </button>
    </div>
  );
});
```

### API

- `createBridge(LocalBridge)` 创建局部桥对象
- `$bridge.install(initalState:State)` 安装并初始化桥对象状态
- `$bridge.connect(option:Array<keyof State> | (state:State) => any)` 监听并返回状态中的值，当 option 为数组时，则返回状态在数组中的对应字段，假如 option 为函数，则使用函数返回值作为监听的数据
- `$bridge.update(nextState: State | (state)=>state )` 更新桥状态，假如是对象，则和原状态进行浅合并，假如是函数，则使用返回值替换原状态，假如函数无返回值，则可以通过直接操作 state 进行更新状态
- `$bridge.xxx = (...args) => any` 挂载方法，允许其他模块通过`$bridge.xxx(...args)`的方式与当前模块进行通信
- `$bridge.onMessage(cmd, callback, delay?:number); $bridge.postMessage(cmd, payload)` 创建消息通信，允许不同模块创建消息监听，可以直接接受任何模块的广播消息并做出响应，广播消息允许发送参数`payload`,假如存在`delay`，那么广播的消息在相应的时间内进行合并处理

#### $bridge.connect(~) 示例，通过 connect 进行状态精确监听，优化渲染性能

```ts
/// state = {name:'张三',age:18,info:{address:'浙江省'}}

// 方式1：返回name,age并且state.name和state.age发生变更时，都会触发函数render
const { name, age } = $bridge.connect(['name', 'age']);

// 方式2：返回并监听state.info.address，仅在address的值发生变化时才触发函数render
const { address } = $bridge.connect((state) => ({
  address: state.info.address,
}));
```

### GlobalBridge（全局级状态管理）

全局应用状态管理会跨页面进行通信，主要用于一些全局状态比如用户信息，应用配置等的数据管理

#### $bridge.ts 声明桥对象

```ts
import { createBridge, GlobalBridge } from '@react-fox/bridge';

const $bridge = createBridge(
  class MyBridge extends GlobalBridge<
    // 状态签名
    {
      name: string;
      age: number;
    },
    // 消息签名
    {
      hello: {
        message: string;
      };
    }
  > {
    toggleTips() {
      // ...
    }
  },
  { name: '张三', age: 18 }
);

export default $bridge;
```

> 全局桥对象不需要`$bridge.install(~)`,初始化状态在 create 的时候作为第二个参数传入，其他接口和`LocalBridge`完全保持一致

### 完美结合 hooks

通过挂载`Dispatcher`或者监听广播`Messager`的方式，可以完美的与`useState`结合进行模块状态控制，通信调用，消息模式可以解耦模块逻辑，不会出现调用不存在的方法导致脚本错误

### 结合 typescript

通过`State`,`Dispatcher`,`Messager`定义签名，完全满足 typescript 的类型检测和自动提示

### 说明

更多示例见 `node_modules/@react-fox/bridge/sample`
使用模板见 `node_modules/@react-fox/bridge/template`

## 完整 API

- createBridge(~) 创建通信桥
- $bridge.connect(~) 创建状态依赖并返回状态值
- $bridge.update(~) 更新状态
- $bridge.xxx(~) 调用 dispatcher 方法，以继承的方式定义
- $bridge.onMessage(~) 创建消息监听
- $bridge.postMessage(~) 广播消息
- LocalBridge 页面桥类
  - $bridge.install(~) 安装桥对象
- GlobalBridge 应用桥类
  - GlobalBridgeProvider 全局桥上下文
- WidgetBridge 组件桥类
  - installBridge 安装桥对象
  - useBridge 使用桥对象