0.0.11 • Published 5 years ago

react-bazaar v0.0.11

Weekly downloads
1
License
MIT
Repository
github
Last release
5 years ago

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.

0.0.11

5 years ago

0.0.10

6 years ago

0.0.9

6 years ago

0.0.8

6 years ago

0.0.7

6 years ago

0.0.6

6 years ago

0.0.5

6 years ago

0.0.4

6 years ago

0.0.3

6 years ago

0.0.2

6 years ago

0.0.1

6 years ago