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

@francisco.ruiz/global-state

v0.2.0

Published

Lightweight package to handle the global state of our React-based-applications via the new React Context API

Downloads

1

Readme

Global State

Lightweight package to handle the global state of our React-based-applications via the new React Context API.

Table of Contents

Motivation

Although right now we have very little needs regarding this topic (we moslty work on business logic-focused products), we have a common need for the reasons we'll review in the sections below.

Current issues

  • At this moment, we have one single way to share data ("global state of our business logic") between different components, but it's performed within the Domain through the streamify decorator.
  • There are some data that we don't want to mix with our business logic: information from the browser, user interface or even user preferences.
  • We use a lot the ugly pattern of passing down data throughout the whole tree, component by componet... annoying right?

Why React Context

  • It's basically React, no any other 3rd-party libraries needed.
  • Ease to use and very well documented.
  • Very little boilerplate (almost none) in order to start using it. So different regarding other 3rd-party libraries like Redux.
  • We avoid unnecessary renders by using a context provider that is a PureComponent and handles the "global state" locally, so that any other unneeded prop is passed down, just consumers will get the new updated values.

API Reference

Regarding this point, it's important to say the final implementation is completely based in a 3rd-party utility: https://github.com/dai-shi/react-context-global-state. That means at this moment it fits the most needs we have an also fulfill all my expectations I've had in this project. However, it doesn't mean we can adapt it by modifying the output of our library, or even create our own rewritten library. At the end of the day, the main ideas stand within that code.

Top-Level Exports

  • createGlobalState, whose input is a plain object, and for each property a context will be created.
    • input: {Object} initialState
    • output: {Object} {StateProvider, StateConsumer, stateItemConsumers, stateItemUpdaters}
  • withGlobalStateFactory, a small utility to, once passed a needed GlobalStateConsumer, wrap any component setting a new prop with the global state value required.

Example of Usage

Install

npm install @francisco.ruiz/global-state --save

Create your own state wrapper

I swear it's the hardest part 😛 Let's create it in the root directory and name it GlobalState:

// GlobalState.js
import {createGlobalState, withGlobalStateFactory} from '@francisco.ruiz/global-state'

// Set your initial state.
const initialState = {
  theme: 'dark',
  language: 'en',
  notifications: []
}

// Create your global state.
const {
  StateProvider: GlobalStateProvider,
  StateConsumer: GlobalStateConsumer,
  stateItemUpdaters
} = createGlobalState(initialState)

// Set your actions in order to update your global state.
const actions = {
  toggleTheme: () => {
    const update = stateItemUpdaters.theme

    update(theme => (theme === 'light' ? 'dark' : 'light'))
  },
  updateLanguage: language => {
    const update = stateItemUpdaters.language

    update(() => language)
  },
  pushNotification: notification => {
    const update = stateItemUpdaters.notifications

    update(notifications => [...notifications, notification])
  }
}

// Generate your own HOC in order to consume easily the global state by any context.
// Call it by passing first the context and then the component: ´withGlobalState('theme', Component)´
const withGlobalState = withGlobalStateFactory(GlobalStateConsumer)

// Then export it all.
export {GlobalStateProvider, GlobalStateConsumer, withGlobalState, actions}

Provide your app

Do it where you render your app, let's say in your main file:

// index.js
import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter} from 'react-router-dom'
import {GlobalStateProvider} from './GlobalState'
import App from './App'
import './index.scss'

ReactDOM.render(
  <GlobalStateProvider>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </GlobalStateProvider>,
  document.getElementById('root')
)

Consume the global state

You can do it in the component you want, but let's watch an example within your app component:

// App.js
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'
import {withGlobalState, actions} from './GlobalState'

function App({title, theme}) {
  const isDark = theme === 'dark'

  return (
    <div className={classNames('mt-App', {
      'is-dark': isDark
    })}>
      <h1 className="mt-App-title">{title}</h1>
      <div className="mt-App-toggleTheme">
        Toggle theme
        <input
          type="checkbox"
          checked={isDark}
          onChange={actions.toggleTheme}
        />
      </div>
    </div>
  )
}

App.propTypes = {
  title: PropTypes.string,
  theme: PropTypes.string
}

App.defaultProps = {
  title: 'Hello World!'
}

export default withGlobalState('theme', App)

And now, enjoy! 🎉

Things to be considered

  • If you're using a BrowserRouter wrapping your app you'll have to place your GlobalStateProvider into an upper level because it uses a PureComponent and it won't let the BrowserRouter to propagate the changes to the app components.
  • Within the context of Schibsted Spain, we recommend to wrap the global state and publish it as a npm package.
  • On the global state creation, try to separate the initial state from the actions and the creation itself in different files.

Demo

You can see it working in the project demo I've made for the Front End Enabler test: https://francisco-ruiz-enabler-test.now.sh/

In this project you can toggle the theme between their light and dark modes and also navigate through the main funnel (list and detail) while the browser scroll is kept at its last position on the same page type.

Documentation

  • https://github.com/reactjs/rfcs/blob/master/text/0002-new-version-of-context.md
  • https://reactjs.org/blog/2018/03/29/react-v-16-3.html#official-context-api
  • https://reactjs.org/docs/context.html
  • https://github.com/dai-shi/react-context-global-state
  • https://medium.com/@dai_shi/react-global-state-by-context-api-5b3efa8acc6b
  • https://github.com/drcmda/react-contextual/blob/master/PITFALLS.md
  • https://frontarm.com/articles/when-context-replaces-redux/
  • https://frontarm.com/articles/react-context-performance/

Next steps

  • Split actions by context.
  • Accept to send multiple global state contexts at a time when calling withGlobalState, for instance: withGlobalState(['theme', 'language'], Component).
  • Generate specific consumer HOCs for each context, e.g.: withTheme, withBrowser.
  • Support decorators and even React Hooks 😄