npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@restate/core

v0.13.5

Published

_Restate_ is a predictable, easy to use, easy to integrate, typesafe state container for [React](https://reactjs.org/).

Downloads

51

Readme

Restate

Restate is a predictable, easy to use, easy to integrate, typesafe state container for React.

Restate follows the three principles of Redux:

  • Single source of truth
  • State is read only
  • Changes are pure, but made in a convenient way using immer.js

Futhermore, Restate

  • provides a nice React-Hook based API to read and update the state
  • is using Typescript to make your application more robust and your development experience more enjoyable
  • provide means to develop asynchronous state changes without the drama
  • makes it easy integrate other components (server, logger, database, router,...) as the state is reactive.
  • dev-tools
  • easy to learn and easy to test

What it looks like...

const store = createStore({
  state: {
    name: "John Snow",
    age: 32
  }
})

const AppStoreProvider = createProvider(store);
const useAppState = createStateHook(AppStoreProvider);
const useNextAppState = createNextHook(AppStoreProvider);

const Name = () => {
  const name = useAppState(state => state.user.name)
  const next = useNextAppState(state => state.user)

  function setName(nextName:string) {
    next(user => user.name = nextName)
  }

  return <input value={name} onChange={e => setName(e.target.value)} />
}

Even the code above looks like JS, it is indeed Typescript. Go on StackBlitz and make some changes to the state, for example change the property user.name to user.firstName. You will see how Typescript is able to pick up those changes and gives you nice error messages.

Documentation

The documentation is also available on Netlify:https://restate.netlify.com/.

Installation

With NPM:

npm install @restate/core --save

or with YARN:

yarn add @restate/core

Store

To get started, you need to create a store:

import { createStore } from "@restate/core"

const store = createStore({
  state: {
    name: "John Snow",
    age: 32
  }
})

Try on StackBlitz!

Provider

To connect our store to the React component tree we need a Provider:

import { createProvider } from "@restate/core"

const AppStoreProvider = createProvider(store) // to provide the store to react

const App: React.FC = () => (
  <AppStoreProvider.Provider value={store}>
    <Hello />
    <Age />
    <NameForm />
  </AppStoreProvider.Provider>
)

Try on StackBlitz!

You can use multiple stores as well.

Read from the state

To read from the state Restate provides you AppStateHooks.

AppStateHooks hooks are use to

  • select properties from your state
  • do some basic computations / transformations
const store = createStore({
  state: {
    user: { name: "John Doe", age: 32 },
    todos: 0
  }
})

const AppStoreProvider = createProvider(store)

// create a `scoped state hook` (we select the `user` object)
const useUserState = createStateHook(AppStoreProvider, state => state.user)

const Hello = () => {
  // select a property from the state
  const name = useUserState(user => user.name)
  // do some basic views/computations/transformations
  const days = useUserState(user => user.age * 365)

  return (
    <h1>
      Hello {name}, you are {days} days old
    </h1>
  )
}

Try on StackBlitz!

Change the state - using hooks

To change the state, we use a Next-Hook.

import { createNextHook } from "@restate/core"

// create a next-hook
const useNextAppState = createNextHook(AppStoreProvider)

The useNextAppState hook takes a selector function to scope the access to our state. In this example the scope is the user object.

The useNextAppState returns a customnext function, which can be use to change the user object:

const NameForm = () => {
  const name = useAppState(state => state.user.name)

  // Creates a `next` function to change the user
  const next = useNextAppState(state => state.user)

  function setName(nextName: string) {
    // The next function provides the current user object as parameter, which we can modify.
    next(user => (user.name = nextName))
  }

  return <input value={name} onChange={e => setName(e.target.value)} />
}

Try on StackBlitz!

Change the state - using actions

Another way to modify your state are Actions.

Actions are forged in an ActionFactory. The ActionFactory is a function that receives - among other things - the next() function to update the store.

An ActionFactory returns a object that holds the all the actions to change the state. Think about actions as "member functions" of your state.

Actions can be asynchronous.

// Action factory
const userActionsFactory = ({ next }: ActionFactoryProps<User>) => ({
  incrementAge() {
    next(user => user.age++)
  },
  decrementAge() {
    next(user => user.age--)
  },
  async fetchData(userId: string) {
    const data = await serverFetchUserData(userId)
    next(user => (user.data = data))
  }
})

The ActionFactory is hooked into React using the createActionsHook:

const useUserActions = createActionsHook(
  AppStoreProvider,
  state => state.user,
  userActionsFactory
)

const Age = () => {
  const userActions = useUserActions()
  return (
    <div>
      <button onClick={userActions.incrementAge}>+</button>
      <button onClick={userActions.decrementAge}>-</button>
    </div>
  )
}

Try on StackBlitz

Change the state - using store.next()

Outside of your component tree you can change the store like this:

store.next(state => {
  state.user.name = "John"
})

Middleware

Middleware are small synchronous functions which intercept state updates. Middleware functions receiving the currentState as well as the nextState. They can change the nextState, if required. If a middleware throws an exception, the state update will be canceled.

Take the ageValidator middleware for example. It ensures, that the user.age property never gets negative.

// Ensures the age will never be < 0
const ageValidator: Middleware<State> = ({ nextState, currentState }) => {
  nextState.age =
    nextState.user.age < 0 ? currentState.user.age : nextState.user.age
}

const store = createStore({
  state: {
    name: "John Snow",
    age: 32
  },
  middleware: [ageValidator]
})

store.next(s => (s.user.age = -1)) // will be intercepted by the ageValidator middleware.

Try on StackBlitz

Connectors

Connectors "glue" your store to other parts of the application, for example to your server, database, ...

Connectors can

  • observer the state and react to state changes using the store.state$ observable
  • change the state using the store.next() function
  • listen to events dispatched on the state.messageBus$ observable. The messages are similar to redux actions.

Observe store.state$

Here is an very simple logger example, that observes the state and logs all state changes:

function connectLogger(store: RxStore<any>) {
  store.state$.subscribe(nextState => {
    console.log("STATE:", JSON.stringify(nextState.payload))
  })
}

connectLogger(store)

Try on StackBlitz

Change the state with store.next()

Another example of a connector could be a socket.io adapter, that receives chat messages from a server and adds them to the application state:

function connectSocket(store: RxStore<any>) {
  socket.on("chat message", msg => {
    store.next(state => {
      state.messages.push(msg)
    })
  })
}

connectSocket(store)

Listen to events

Connectors can also receive messages from the application - redux style.

Here is a simple UNDO example. The connector records the history of the app state using the store.state$ observable. The connector also listens to the UNDO events by subscribing the store.messageBus$. If it receives the UNDO event, it rewinds the state history by one step.

  const history = []

  // record state history
  store.state$.subscribe(nextState => {
    history.push(nextState);
  })

  // listen to UNDO events
  store.messageBus$.subscribe( msg => {
    if(msg.type === 'UNDO' && history.length > 1) {
      history.pop()  // remove current state
      const prevState = history.pop();
      store.next(prevState);
    }
  })
}

connectUndo(store);

The application uses createDispatchHook to create a dispatch hook. With the dispatch hook, a component can dispatch an UNDO event, like so:

const useDispatch = createDispatchHook(AppStoreProvider)

function useUndo() {
  const dispatch = useDispatch()
  return () => dispatch({ type: "UNDO" })
}

const UndoButton = () => {
  const undo = useUndo()
  return <button onClick={undo}>UNDO</button>
}

Try the UNDO example on StackBlitz!

DevTools

restate uses the excellent ReduxDevTools to provide power-ups for your development workflow.

DevTools screenshot

Installation

Go and get the ReduxDevTools for your browser:

  • Google Chrome
  • Firefox

Then install the @restate/dev-tools

yarn add @restate/dev-tools

Usage

import { connectDevTools } from "@restate/dev-tools"

const store = createStore({
  state: {
    name: "John Snow",
    age: 32
  },
  options: {
    storeName: "MY APP STORE" // <-- will show up in the instance selector
  }
})

connectDevTools(store)

License

MIT