8.5.1 • Published 5 months ago

@equinor/fusion-observable v8.5.1

Weekly downloads
-
License
ISC
Repository
github
Last release
5 months ago

Observable

isObservableInput

A utility to check if a value is an observable-like input (Observable, Promise, or Iterable). Useful for writing flexible APIs or utilities that can accept a wide range of input types.

Example:

import { isObservableInput } from '@equinor/fusion-observable';
import { of } from 'rxjs';

isObservableInput(Promise.resolve(1)); // true
isObservableInput([1, 2, 3]); // true
isObservableInput(of(42)); // true
isObservableInput({}); // false

toObservable

import { toObservable } from '@equinor/fusion-observable';
A utility to convert various input types (value, function, promise, observable, iterable) into a consistent Observable stream. This allows you to handle inputs uniformly without worrying about their original type.
**Example:**

```ts
import { lastValueFrom, Observable } from 'rxjs';
import { toObservable, type DynamicInputValue } from '@equinor/fusion-observable';

const flexibleHandler = async (input: DynamicInputValue<{foo: string}>) => {
  return lastValueFrom(toObservable(input));
};

// All of these work the same way:
flexibleHandler(Promise.resolve({foo: 'bar'})).then(console.log); // {foo: 'bar'}
flexibleHandler({foo: 'baz'}).then(console.log); // {foo: 'baz'}
flexibleHandler(() => ({foo: 'qux'})).then(console.log); // {foo: 'qux'}
flexibleHandler(async () => ({foo: 'async'})).then(console.log); // {foo: 'async'}
flexibleHandler(new Observable((subscriber) => {
  subscriber.next({foo: 'bar'}); // skipped
  subscriber.next({foo: 'baz'});
  subscriber.complete();
})).then(console.log); // {foo: 'baz'}

Works with iterables and async generators too:

function* gen() {
  yield 1;
  yield 2;
}
toObservable(gen()).subscribe(console.log); // 1, 2

async function* asyncGen() {
  yield 'a';
  yield 'b';
}
toObservable(asyncGen()).subscribe(console.log); // 'a', 'b'

React

Observable

type TodoItem = {
  id: string;
  txt: string;
}

type State = Record<string, TodoItem>;

enum ActionType {
  ADD = 'add',
  REMOVE = 'remove'
}

type ActionAdd = {
  type: ActionType.ADD,
  payload: Omit<TodoItem, 'id'>;
}

type ActionRemove = {
  type: ActionType.REMOVE
  payload: string;
}

type Actions = ActionAdd | ActionRemove

const reducer = (state: State, action: Actions) => {
  switch(action) {
    case ActionType.ADD:
      return {
        ...state,
        [generateId()]: action.payload
      };

    case ActionType.REMOVE:{
      const next = {...state};
      delete next[action.payload];
      return next;
    }
  }
};

const TodoContext = React.createContext();

export const TodoListProvider = (
  options: React.PropsWithChildren<{ initial: State }>
) => {

  const [selected, setSelected] = useState<string|undefined>();
  const state$ = useObservable(reducer, initial);

  const add = useCallback((item: Emit<TodoItem, 'id'>) => {
    state$.next({ type: ActionType.ADD, payload: item });
  }, [state$]);

  const remove = useCallback((id: string) => {
    state$.next({ type: ActionType.REMOVE, payload: item });
  }, [state$])
  
  return (
    <TodoContext.Provider value={ {state$, add, remove, selected, setSelected} }>
      { options.children }
    </TodoContext.Provider>
  );
}

const TodoDetail = () => {
  const { state$, selected } = useContext(TodoContext);
  const item = useObservableSelectorState(state$, selected)
  return item ? (
    <div>
      <span>ID: {item.id}</span>
      <span>TXT: {item.txt}</span>
    </div>
  ) : null;
}

const AddTodo = () => {
  const { add } = useContext(TodoContext);
  const txtRef = useRef(null);
  const onSubmit = useCallback(() => {
    add({ txt: txtRef.current.value })
  }, [add, txtRef]);
  return (
    <div>
      <input ref={ txtRef } />
      <button onClick={ onSubmit } />
    </div>
  );
};

const TodoList = () => {
  const { state$, setSelected } = useContext(TodoContext);
  const items = useObservableState(state$).next;
  return (
    <ul>
      { Object.entries(items).map(
        ([key,value]) => <li key={key} onClick={ () => setSelected(key) }>{value.txt}</li>
      )}
    </ul>
  )
}

const App = () => {
  return (
    <TodoListProvider initial={ {} }>
      <TodoDetail />
      <TodoList />
      <AddTodo />
    </TodoListProvider>
  )
}
8.4.5

8 months ago

8.4.4

9 months ago

8.4.7

6 months ago

8.4.6

7 months ago

8.4.3

11 months ago

8.4.2

11 months ago

8.4.9

5 months ago

8.4.8

6 months ago

8.5.0

5 months ago

8.5.1

5 months ago

8.4.1

1 year ago

8.4.0

1 year ago

8.3.2

1 year ago

8.3.1

1 year ago

8.3.3

1 year ago

8.3.0

2 years ago

8.2.0

2 years ago

8.1.5

2 years ago

8.1.4

2 years ago

8.1.3

2 years ago

8.1.0

2 years ago

8.1.2

2 years ago

8.1.1

2 years ago

8.0.3

2 years ago

7.0.0

3 years ago

7.0.3

3 years ago

7.0.2

3 years ago

7.0.1

3 years ago

8.0.1

2 years ago

8.0.0

3 years ago

8.0.2

2 years ago

3.0.4

3 years ago

3.0.3

3 years ago

3.0.2

3 years ago

3.0.1

3 years ago

4.0.1

3 years ago

4.0.0

3 years ago

6.0.0

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.0.0

3 years ago

1.5.1

3 years ago

1.5.0

3 years ago

1.4.1

3 years ago

1.4.0

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.1

3 years ago

2.3.0

3 years ago

2.1.2

3 years ago

2.2.0

3 years ago

2.5.0

3 years ago

2.4.1

3 years ago

2.4.0

3 years ago

2.4.2

3 years ago

2.1.0

3 years ago

2.0.1

3 years ago

3.0.0

3 years ago

0.3.2

3 years ago

0.4.0

3 years ago

0.3.1

3 years ago

0.3.3

3 years ago

0.1.10

3 years ago

0.1.11

3 years ago

0.1.12

3 years ago

0.3.0

3 years ago

0.1.2

3 years ago

0.2.0

3 years ago

0.1.8

3 years ago

0.1.7

3 years ago

0.1.9

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.6

3 years ago

0.1.5

3 years ago

0.1.1

3 years ago

0.1.0

3 years ago

0.0.2

4 years ago

0.0.1

4 years ago