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

react-bazaar

v0.0.11

Published

A simple boilerplate-less type-safe state store for react

Downloads

5

Readme

react-bazaar

A tiny wrapper for the new React (> 16.3) context API.

This library was written with the goal of easing the use of the context API, providing a no-boilerplate, completely type-safe, state store for react components.

The code samples are TypeScript, but the library works the same with Vanilla JS.

Only compatible with React >16.3

How to use?

Install

npm install react-bazaar

Define your state interface and initial-state object


interface ITodo {
    id: number,
    description: string,
    done: boolean
}

interface IState {
    todos: Array<ITodo>
}

const initialState : IState = {
    todos: [
        { id: 1, description: "Learn Typescript", done: true },
        { id: 2, description: "Use React-Bazaar", done: false }
    ]
}

Create a wrapped context object

import { createBazaar } from "react-bazaar"
const context = createBazaar(initialState)

The wrapped context object contains a Provider and a Consumer component. The Provider-component is only needed at the root component from where you want to propagate state. The Consumer-component is needed where-ever you want to consume the global state.

Create the root component

const App = () => (
    <context.Provider>
        // ... the rest of your app / components
    </context.Provider>
)

Consume the state

Render the consumer and give it a function that accepts a state object as first parameter.

// a dumb Todo-Component
interface ITodoComponentProps { todo: ITodo }
const Todo = (props: ITodoComponentProps) =>
    <div style={{textDecoration: props.todo.done ? "line-through" : "none"}}>
        {props.todo.description}
    </div>

// a list of dumb Todo-Components, rendered from the todos in the store
const Todos = () => (
    <context.Consumer>
        {state => state.todos.map(todo => <Todo todo={todo} />)}
    </context.Consumer>
)

The state's inferred type will be that of IState so you will have complete type-safety and auto-completion.

Update the state

So far, this has just been the normal behaviour of the Context API. The difference lies in the fact that react-bazaar also offers a way to update the global state.

First, create an action. This is a function that receives the global state as first parameter and returns a changed state object. State changes must happen immutable. For convenience, we wrap a function around the action which takes some parameters.

(In this example I use the immutability helper library immer, which provides an easy way to do an immutable change by doing a mutation on a proxied object.)

import { Action } from "react-bazaar"
import produce from "immer"
const toggleTodo = (todoId: number) : Action<IState> => state => produce(state, state =>
{
	const todoToToggle = state.todos.find(todo => todo.id === todoId)
		if(todoToToggle)
			todoToToggle.done = !todoToToggle.done
		return state
})

We'll reuse the previous component but add the toggle functionality to it.

interface ITodoComponentProps { todo: ITodo, toggle: { () : void } }
const Todo = (props: ITodoComponentProps) =>
    <div
        onClick={props.toggle}
        style={{textDecoration: props.todo.done ? "underline" : "none"}}>
        {props.todo.description}
    </div>

const Todos = () => (
    <context.Consumer>
        {
            (state, dispatch) =>
                state.todos.map(todo => <Todo todo={todo} toggle={() => dispatch(toggleTodo(todo.id))}/>)
        }
    </context.Consumer>
)

As you can see, the render props function of the consumer receives a second parameter dispatch. This is a function which accepts an Action and will update the state.

Middleware

When creating the context object with createBazaar, you have the option to pass in an array of middleware functions.

const logDelimiter : Middleware<IState> = action => state =>
{
    console.log("----------------------")
    const newState = action(state)
    console.log("----------------------")
    return newState
}

const durationLogger : Middleware<IState> = action => state =>
{
    const before = new Date().getTime()
    const newState = action(state)
    const after = new Date().getTime()
    const delta = after - before
    console.log(`Action took ${delta} milliseconds.`)
    return newState
}

const changeLogger : Middleware<IState> = action => state =>
{
    console.log("state before action", state)
    const newState = action(state)
    console.log("state after action", newState)
    return newState
}

const context = createBazaar(initialState, [logDelimiter, durationLogger, changeLogger])

Tips & Tricks

Server Side Rendering

If you want to prefill your store on the server you can just call your actions on your initialState object manually, serialize the result, pass it to the client and use that as your initialState.

let state = initialState
state = toggleTodo(1)(state)
state = toggleTodo(2)(state)
// ...
// use state as your initial store state

That's it

That's all there is to it. Enjoy. Inspired by redux and react-contextual.

Flow-typings-PR welcome.