@equinor/fusion-observable

中文文档
9.2.6发布于 2026/11/12ISCTypeScriptES Module近 30 天下载 1.5万

WIP

原始使用文档
文档目录14 节
  1. Features
  2. Installation
  3. Usage
  4. Creating a state container
  5. Using createState for quick setup
  6. Async actions
  7. Normalising inputs with toObservable
  8. React hooks
  9. RxJS operators
  10. API Reference
  11. Core ( @equinor/fusion-observable )
  12. Operators ( @equinor/fusion-observable/operators )
  13. React hooks ( @equinor/fusion-observable/react )
  14. Actions ( @equinor/fusion-observable/actions )

@equinor/fusion-observable

Reactive state management utilities built on RxJS and Immer for Fusion Framework applications. Provides an action-driven, observable state container (FlowSubject), action creators, reducer builders, RxJS operators, and React hooks for subscribing to observable state.

Features

  • FlowSubject — an observable state container that processes actions through a reducer, similar to Redux but fully reactive with RxJS.
  • createReducer — builds immutable reducers with Immer-powered draft mutations and a builder-pattern API (addCase, addMatcher, addDefaultCase).
  • createAction / createAsyncAction — type-safe action creator factories with optional payload preparation, including async request/success/failure patterns.
  • createState — convenience factory that wires a FlowSubject, action definitions, and dispatch functions in one call.
  • RxJS operators — filterAction, mapAction, switchMapAction, mapProp for concise action-stream transformations.
  • React hooks — useObservable, useObservableState, useObservableSelector, useObservableEffect, useObservableFlow, useDebounce, and more.
  • Utility functions — isObservableInput and toObservable for normalising diverse input types into RxJS observables.

Installation

pnpm add @equinor/fusion-observable

Usage

Creating a state container

import { createAction, createReducer, FlowSubject } from '@equinor/fusion-observable';

// Define actions
const increment = createAction<number>('increment');
const decrement = createAction<number>('decrement');

// Build a reducer with Immer support
const reducer = createReducer({ count: 0 }, (builder) =>
  builder
    .addCase(increment, (state, action) => { state.count += action.payload; })
    .addCase(decrement, (state, action) => { state.count -= action.payload; }),
);

// Create the observable state container
const counter = new FlowSubject(reducer);
counter.subscribe((state) => console.log('Count:', state.count));
counter.next(increment(1)); // Count: 1
counter.next(increment(5)); // Count: 6

Using createState for quick setup

import { createState, createAction } from '@equinor/fusion-observable';

const actions = {
  setName: createAction<string>('setName'),
};

const { subject, dispatch } = createState(actions, {
  initial: { name: '' },
  builder: (builder, actions) => {
    builder.addCase(actions.setName, (state, action) => {
      state.name = action.payload;
    });
  },
});

dispatch.setName('Alice');
console.log(subject.value); // { name: 'Alice' }

Async actions

import { createAsyncAction, isSuccessAction, isFailureAction } from '@equinor/fusion-observable';

const fetchUser = createAsyncAction(
  'fetchUser',
  (id: string) => ({ payload: { id } }),
  (user: User) => ({ payload: user }),
  (error: Error) => ({ payload: error }),
);

// fetchUser('123')           → { type: 'fetchUser::request', payload: { id: '123' } }
// fetchUser.success(user)    → { type: 'fetchUser::success', payload: user }
// fetchUser.failure(error)   → { type: 'fetchUser::failure', payload: error }

Normalising inputs with toObservable

import { toObservable } from '@equinor/fusion-observable';

// Works with values, promises, functions, iterables, and observables
toObservable(42).subscribe(console.log);                        // 42
toObservable(Promise.resolve('hello')).subscribe(console.log);  // 'hello'
toObservable(() => 'computed').subscribe(console.log);           // 'computed'

React hooks

import { useObservable, useObservableState } from '@equinor/fusion-observable/react';

function Counter() {
  const subject = useObservable(reducer, { count: 0 });
  const { value } = useObservableState(subject);

  return (
    <div>
      <p>Count: {value.count}</p>
      <button onClick={() => subject.next(increment(1))}>+1</button>
    </div>
  );
}

RxJS operators

import { filterAction, mapAction, switchMapAction } from '@equinor/fusion-observable/operators';

// Filter to specific actions
action$.pipe(filterAction('increment')).subscribe(handleIncrement);

// Filter + map in one step
action$.pipe(mapAction('fetchSuccess', (a) => a.payload.data)).subscribe(setData);

// Filter + switchMap for async flows
action$.pipe(switchMapAction('search', (a) => fetchResults(a.payload))).subscribe(setResults);

API Reference

Core (@equinor/fusion-observable)

ExportDescription
FlowSubject<S, A>Observable state container driven by actions and a reducer
createReducer(initial, builder)Builds an Immer-powered reducer with builder pattern
createAction<P>(type)Creates a type-safe action creator
createAsyncAction(type, request, success, failure?)Creates request/success/failure action creators
createState(actions, reducer)Wires FlowSubject + actions + dispatch in one call
actionMapper(actions, subject)Binds action creators to a subject as dispatch functions
ActionErrorError class linking an action to a causal error
ActionReducerMapBuilderBuilder interface for defining case reducers
enableMapSet()Enables Immer support for drafting Map and Set values
isObservableInput(input)Type guard for RxJS ObservableInput values
toObservable(input, ...args)Converts values/functions/promises to Observable

Operators (@equinor/fusion-observable/operators)

ExportDescription
filterAction(...types)Filters an action stream by type(s)
mapAction(type, fn)Filters by type and maps the result
switchMapAction(type, fn)Filters by type and switchMaps to an inner observable
mapProp(path)Extracts a nested property via a dot-path string

React hooks (@equinor/fusion-observable/react)

ExportDescription
useObservable(reducer, initial?)Creates a memoised FlowSubject
useObservableState(subject, options?)Tracks value/error/complete of an observable
useObservableSelector(subject, selector)Derives a child observable with distinctUntilChanged
useObservableEffect(subject, effect)Attaches a side-effect to a FlowSubject
useObservableFlow(subject, flow)Attaches an epic-style flow to a FlowSubject
useObservableSubscription(obs, observer)Manages an observable subscription lifecycle
useObservableRef(subject)Keeps a ref synchronised with observable emissions
useObservableInput(input)Converts ObservableInput to Observable
useObservableInputState(input)Combines useObservableInput + useObservableState
useDebounce(fn, options)Debounces a function and exposes results as an observable

Actions (@equinor/fusion-observable/actions)

Re-exports action types, ActionError, and action creator utilities for consumers that only need the action layer.

版本与发布记录

@equinor/fusion-observable 共发布 121 个版本,首次发布于 2022/04/11;过去 12 个月发布了 23 个版本。

版本发布日期距今
9.2.6latest2026/11/121 天前
9.2.6-next.0next2026/11/112 天前
9.2.52026/10/1727 天前
9.2.5-trusted-publishing-20261015-9dfe7602cd2a49a8a9df00d62c26e7304a82a85atrusted-publishing-202610152026/10/1629 天前
9.2.42026/10/121 个月前
9.2.32026/09/261 个月前

其他发布通道:apploader → 8.4.10-apploader-be21a6b60eb13eee8bf869e75002d8634e9c50a9,cli-search-index → 8.5.7-cli-search-index.0,msal-v5 → 8.5.8-msal-v5.0,trusted-publishing-20261015 → 9.2.5-trusted-publishing-20261015-9dfe7602cd2a49a8a9df00d62c26e7304a82a85a,next → 9.2.6-next.0。

关于 @equinor/fusion-observable 的常见问题

@equinor/fusion-observable 是什么?

WIP

如何安装 @equinor/fusion-observable?

在项目目录运行 npm install @equinor/fusion-observable,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:react(^18.0.0 || ^19.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@equinor/fusion-observable 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@equinor/fusion-observable 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@equinor/fusion-observable 可以商用吗?

@equinor/fusion-observable 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@equinor/fusion-observable 的下载量有多少?

根据 npm 官方下载统计,@equinor/fusion-observable 在 2026/10/14 – 2026/11/12 这 30 天内被下载 14,860 次,日均约 495 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@equinor/fusion-observable 还在维护吗?

最新版本 9.2.6 发布于 2026/11/12(1 天前);至今共发布 121 个版本,其中过去 12 个月发布了 23 个;首次发布于 2022/04/11。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。