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-updatable-context

v1.1.0

Published

Regular React context with setter/updater method injected in consumer

Downloads

54

Readme

React Updatable Context

Allow to easily update React context Provider value, directly from the consumer.

This makes the Provider stateful.

yarn add react-updatable-context
npm install react-updatable-context

Examples

Let's first define a very simple language selector component, and in the following examples we'll see how to connect this stateless component to our context.

const LanguageSelector = ({language, updateLanguage}) => (
  <div>
    <div>Current language: {language}</div>
    <div onClick={() => updateLanguage("french")}>Switch to french</div>
    <div onClick={() => updateLanguage("english")}>Switch to english</div>
  </div>
);

Simple example


import { createUpdatableContext } from 'react-updatable-context';

const { Provider: LanguageProvider, Consumer: LanguageConsumer } = createUpdatableContext(); 

// Add the provider to your app like with normal Context api
const MyApp = () => (
  <LanguageProvider 
    initialValue={"english"} 
    afterChange={(newLanguage,oldLanguage) => console.log("Language has been updated: " + newLanguage,oldLanguage)}
  >
    <Router/>
  </LanguageProvider>
);

// Use the consumer like normal Context api, but it also receives a 2nd parameter
const AppLanguageSelector = () => (
  <LanguageConsumer>
    {(value, update) => (
      <LanguageSelector language={value} updateLanguage={update} />
    )}
  </LanguageConsumer>
)

Managing your global app state with Context

Sometimes the level of indirection with events/actions, and tools like Redux, is too much. Using a simple global state API on top of Context can be good enough for many apps.

But... We don't want to expose the state structure to deeply nested components.

createSubConsumer and connect are here for that.


import { createUpdatableContext } from 'react-updatable-context';

const {
  Provider, 
  Consumer,
  createSubConsumer,
  connect,
 } = createUpdatableContext({
  defaultValue: null, // You can set default value, like regular React context
}); 


const MyApp = () => (
  <Provider 
    initialValue={{
      language: "english",
      unreadChatCount: 0,
    }} 
  >
    <Router/>
  </Provider>
);


// Then you can use the global consumer directly, but your component is aware of the global state structure
const AppLanguageSelector = () => (
  <Consumer>
    {(value, update) => (
      <LanguageSelector 
        language={value.language} 
        updateLanguage={language => update({language})} 
      />
    )}
  </Consumer>
);

// You can use a "sub consumer" which will refine the value/update api so that you can only get/set the language
const LanguageConsumer = createSubConsumer(
  value => value.language, // mapValue (1st arg), it's a "selector" so you can use reselect if you want
  update => language => update({language}), // mapUpdate (2nd arg)
);

// Then you can write this instead, which expose less internal state structure
const AppLanguageSelector = () => (
  <LanguageConsumer>
    {(value, update) => (
      <LanguageSelector 
        language={value} 
        updateLanguage={update} 
      />
    )}
  </LanguageConsumer>
);

// You can also use `connect` if you prefer HOCs and an API similar to react-redux
const AppLanguageSelector = connect(
  (value,update) => ({
    language: value.language,
    updateLanguage: language => update({language}),
  }),
)(LanguageSelector)

Performance and pure components

For performance reasons, we want to inject stable callbacks when connecting to components.

On every global state changes, all the consumers will be invoked, and we don't want pure components that consume only a small amount of state to render unnecessarily.

For that, it is possible to create the callbacks only once, at creation time.

Somehow, you are defining an update API that will replace the raw low-level update API that is injected as the 2nd arg of the consumer

const {
  // ...
  connect,
 } = createUpdatableContext({
  defaultValue: null,
  
  // This creates an "update API"
  mapUpdate: (update,getValue) => ({
    updateLanguage: language => update({language}),
    incrementUnreadChatCount: () => update({unreadChatCount: getValue().unreadChatCount + 1}),
  }),
}); 

// Then you can connect with a stable callback:
const AppLanguageSelector = connect(
  (value,updateApi) => ({
    language: value.language,
    updateLanguage: updateApi.updateLanguage,
  }),
)(LanguageSelector)

// Or with a sub-consumer
const LanguageConsumer = createSubConsumer(
  value => value.language,
  updateApi => language => updateApi.updateLanguage(language),
);

Advanced: using a reducer

TODO reducer/dispatch example

TODO

  • Complete examples
  • Support updates with a function (like setState(oldState => newState))