Skip to content

jkeegan2/fysics

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

96 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

fysics

fast blue glowing lines in a grid network

fysics

Minimal State Management for TypeScript & JavaScript Applications.

NPM version HEAD Build Status Release Build Status semantic-release: conventionalcommits Test Coverage Maintainability Contributors Package size Version Support code style: prettier


Contents

Features ✨

  • Define Store

Install πŸ™

If using npm:

npm install fysics

If using yarn:

yarn install fysics

If using pnpm:

pnpm install fysics

Usage πŸ’‘

Fysics is a powerful state management tool. It seeks to work as a more minimalist version of Redux with undo/redo history and side effects following the redux-saga pattern, powered by RxJS and Immer. Decide upon your types for State, Payload, and SagaResponse, write the logic for initial state and actions, initialize the store, and go!

User-defined Types

State

This is the type describing the contents of the store.

Example:

type StateType = {
  count: number;
};

While it is possible to add additional properties to the State after the store is initialized, it's recommended that the type used here contains all the properties you ever expect to have in your store. This better follows the intended state management design pattern, and will give the full benefits of using Typescript.

Payload

This is the type describing the parameter that is passed to the saga and/or reducer.

Example:

type PayloadType = {
  amount: number | undefined;
};

Each action may have its own payload shape and corresponding type - the Payload type should be a union of all of these, and within the code for the reducer and/or saga, the payload should be typecast to the type of the expected payload.

SagaResponse

This is the type describing the return value from the saga, which is optionally passed to the reducer.

Example:

type SagaResponseType = number;

As with the payload, each action that has a saga may have its own sagaResponse type. The SagaResponse type should be a union of all of these, and within the code for the reducer, the sagaResponse should be typecast to the type of the expected sagaResponse.


Logic

The Logic is where the actions and initial state of your store are defined. The type is below:

type Logic<State, Payload, SagaResponse> = {
  initialState: State;
  actions: {
    [ACTION_NAME: string]: Action<State, Payload, SagaResponse>;
  };
};

initialState

The initialState property should contain initial values for every property you plan on having in the store. Even if the value is undefined, the property should still be listed explicitly.

Actions

Each property is a name of an action, with the corresponding value describing that action using the Action type:

type Action<State, Payload, SagaResponse> = {
  skipUndo?: boolean;
  scope?: string | Array<string>;
  reducer: Reducer<State, Payload, SagaResponse>;
  saga?: Saga<State, Payload, SagaResponse>;
};

The only required property for an Action is the Reducer, which is a function modifying a draft version of the current state following some logic, potentially using the payload and sagaResponse passed in.

type Reducer<State, Payload, SagaResponse> = (
  state: Draft<State>,
  payload: Payload,
  sagaResponse?: SagaResponse,
) => void;

If the action has some side effect, the side effect is handled using a Saga:

type Saga<State, Payload, SagaResponse> = (
  state: State,
  payload: Payload,
) => Promise<SagaResponse>;

The saga runs before the reducer to perform some (usually asynchronous) action outside the scope of the store, for example writing to local storage or querying a database. The return value of the saga is then passed to the reducer of this action in the case it has an impact on the state.

If the action should not be considered as an individual action to be undone or redone, set skipUndo to true. For example, if this action happens in the background without the user's knowledge after the most recent action the user has performed, initiating an undo should undo both the background action and the last action the user actually initiated.

The scope property is used for abstracting parts of the logic away from individual actions - it is not used by the store itself, but may be useful inside reducers or sagas to avoid code duplication.


The store implements the Svelte store contract so the $ syntactic sugar is available.

Documentation πŸ“„

The Store returned by the createStore function is contains methods to interface with two internal components: the store's present state (of type State), and the store's full state, which is defined as

FullState<State, Payload>;

where

type FullState<State, Payload> = {
  past: Array<TimelineEvent<PayloadType>>;
  present: State;
  future: Array<TimelineEvent<PayloadType>>;
};

type TimelineEvent<Payload> = {
  action: DispatchAction<Payload>;
  patches: Array<Patch>;
  inversePatches: Array<Patch>;
};

type DispatchAction<Payload> = {
  name: string;
  payload?: Payload;
};

are the relevant type definitions. See here to understand the Patch type imported from immer.

subject

subject: () => Subject<State>;

Gets the subject for the store's present state.

subjectAll

subject: () => Subject<FullState<State, Payload>>;

Gets the subject for the store's full state.

subscribe

subscribe: (observer?: Partial<Observer<State>> | ((value: State) => void)) =>
  Subscription;

Creates a subscription to the store's present state with the specified observer or function to run when the store's present state subject pushes a new value (i.e. when the store's present state changes).

subscribeAll

subscribe: (observer?: Partial<Observer<State>> | ((value: State) => void)) =>
  Subscription;

Creates a subscription to the store's full state with the specified observer or function to run when the store's full state subject pushes a new value (i.e. when the store's full state changes).

get

get: () => State;

Returns a deep copy of the store's present state.

getAll

getAll: () => FullState<State, Payload>;

Returns a deep copy of the store's full state.

dispatch

dispatch: (action: DispatchAction<Payload>) => void

Dispatches an Action (see the Usage section) by name, with the provided payload.

undo

undo: () => void

Undoes actions in the store's past until an action with skipUndo: false is undone, or does nothing if all the actions in the store's past have skipUndo: true.

redo

redo: () => void

Redoes actions in the store's future until the second most recent action with skipUndo: false is on top of the stack, or redoes everything if there is no such action.

rebase

rebase: () => void

Clears the past and future of the store's full state.

Contributing 🍰

Please make sure to read the Contributing Guide before making a pull request.

Thank you to all the people who already contributed to this project!

Maintainers πŸ‘·

Anubis
Anubis

πŸ’»
jkeegan2
jkeegan2

πŸ’»

License βš–οΈ

MIT

About

No description, website, or topics provided.

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 99.1%
  • Shell 0.9%