0.1.6 • Published 3 years ago

react-use-token v0.1.6

Weekly downloads
45
License
MIT
Repository
github
Last release
3 years ago

react-use-token - Experimental

Token based state management for react:

  • Simple react hooks API
  • Easy delegation of part of state to other components
  • Granular state observability, only re-renders affected components
  • Immutable state
  • Extensible
  • Strongly typed :heart: Typescript

Installation

# npm
npm install react-use-token

# yarn
yarn add react-use-token

Problem

React state hooks problems:

  • It couples the component that invokes the hook to the state, re-rendering it every time the state changes
  • Lot of boilerplate code to delegate part of the state to other components

View in code sandbox

Solution

  • Use tokens to decouple the creation of state from it's usage
  • Allow tokens to be decomposed into child tokens to delegate part of the state
  • Strongly type tokens so you know what's the type of the state the token represents

View in code sandbox. Only required components re-render, and it's much simpler to delegate part of the state to other componentes.

State tokens API

Create a state token:

const stateToken = useStateToken({ firstName: '', lastName: '' });
// typeof stateToken: Token<State<{ firstName: string, lastName: string }>>

Create state token from value initializer.:

const stateToken = useStateToken(() => ({ firstName: '', lastName: '' }));
// typeof stateToken: Token<State<{ firstName: string, lastName: string }>>

Decompose state token:

const firsNameToken = stateToken.firstName;
// typeof firsNameToken: Token<State<string>>

Redeem token value:

const value = useTokenValue(stateToken.firstName);
// typeof value: string

Redeem token setter:

const setValue = useTokenSetter(stateToken.firstName);
// typeof setValue: (v: string) => void
setValue(10);

Redeem token state (value + setter):

const [value, setValue] = useTokenState(stateToken.firstName);

Get value without hooks (use it inside effects or callbacks):

const value = getTokenValue(stateToken.firstName);
// typeof value: string

Set value without hooks (use it anywhere):

setTokenValue(stateToken.firstName, 10);

Tokens are just a definition of how to get and set state, they can outlive the state value:

const token = useStateToken<any>(null);
useEffect(() => {
  setTokenValue(token.user.preferences.languages[0], "EN");
  // token value: { user: { preferences: { languages: ["EN"] } } }
, []);

Form tokens API

Form tokens extends state tokens with validation and useful metadata to build forms.

Form tokens are tightly integrated with Yup schemas.

Usage:

// Declare form schema using yup
const schema = object({
  firstName: string().label("First name").required(),
  lastName: string().label("Last name").required()
});
...
// Create form token
const formToken = useFormToken({ schema });
...
// Delegate part of state to other components
<TextField token={formToken.firstName} />
<TextField token={formToken.lastName} />

View in code sandbox

All state tokens hooks/functions are also available in form tokens.

Redeem schema information:

const { label, required } = useTokenSchemaInfo(formToken.firstName); // label: First name, required: true

Redeem validation error:

const error = useTokenErrorMessage(formToken.firstName); // Returns: Name is required

Redeem validation status:

const validationStatus = useTokenValidationStatus(formToken); // Type: 'pending' | 'validating' | 'invalid' | 'valid'

Types guide

A decomposable token is represented with type Token<Feature1 & Feature2...>. It has all possible child tokens exposed as fields. Example:

function (token: Token<Feature1 & Feature2>) {
  var childToken = token.child;
}

Tokens with any types have less child tokens available, making them not assignable to a more specific token type. Example: Token<ReadState<any>> is not assignable to Token<ReadState<string>>.

If you want to support any types, you can use PartialToken<Feature1 & Feature2...>, which is a more compatible token that doesn't have child token fields. You can convert PartialToken<Features> to Token<Features> by calling method restoreToken(partialToken). Example:

function (partialToken: PartialToken<Feature1 & Feature2>) {
  var token = restoreToken(token);
}

Feature types:

TypeDescription
State<TRead, TWrite>State reading and writing
State<TState>Alias to State<TState, TState>
PathRetrieve token path
SchemaRetrieve token schema info
ErrorMessageRetrieve token validation error message
ValidationStatusRetrieve token validation status
ValidatedAlias to Path & ErrorMessage & ValidationStatus
FormState<TRead, TWrite>Alias to State<TRead, TWrite> & Validated<TRead> & Schema<TRead>
FormState<TState>Alias to FormState<TState, TState>

Extensibility

All features from this library are built as composable token extensions.

Instead of using the friendly useStateToken or useFormToken hooks, you could build a token with the same features using the token extensibility API:

const token = useToken();

const tokenWithState = useTokenExtension(token, () => addState<MyState>({}));

const formToken = useTokenExtension(tokenWithState, () => addForm({ schema }));

Learn how to create your own extensions by reading our source code:

Custom extensions examples:

0.1.6

3 years ago

0.1.5

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.2

3 years ago

0.1.1

3 years ago

0.1.0

3 years ago