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

use-context-selection

v1.5.3

Published

Improved hook to select partial data from your Context and get updates on your components only when that specific piece of data changes.

Downloads

49

Readme

use-context-selection

IMPORTANT NOTE!

This library won't work as expected for React >= 18, as the unstable_changedBits property from createContext is no longer supported.

The recommendation is to migrate to use-context-selector library instead, which uses a different approach for scheduling updates when a part of your whole state gets updated.

Install

npm install --save use-context-selection

What?

use-context-selection allows your components to select partial data from Context and receive updates only when that part of your Context value changes.

Why?

By using useContext hook your components subscribes to the entire Context value and will receive updates anytime one of the values in the store changes.

As an example, let's suppose you have data a, b and c stored in your context, and 3 components A, B and C which respectively access that data. Then, suppose that a value in the Context is modified; this will trigger a re-render on the tree components (A, B and C).

This might not be an issue if your application only connects to the Context from a small number of components, but things can get slowish for other applications which connect a lot of components with the store.

To overcome this issue, useContextSelection let you subscribe to any piece of data on your Context and dispatch updates to your components only when it's related data changes! This means, in the example above, changes on a will result in ONLY component A to be re-rendered.

How?

This library makes use of a non-documented feature available on React.createContext API which allows us to disable dispatching updates to every component accessing a Context value. Then, thanks to hooks, we can dispatch updates specifically to the components listening for some changes.

When useContextSelection is used on a component it will register this component as a listener of the Context; then, when a Context value is updated it will detect the changed data and dispatch an update to the components listening for the specific piece of data.

useContextSelection receives a getter function as argument to get whatever you want from the Context; e.g.:

// Let's suppose this is the current state of your Context data
const state = {
  a: 'A value',
  b: 'B value',
  c: 'B value',
}

// Then, in component `A` you can select (and listen) only `a` value
const a = useContextSelection(state => state.a);

And that's it! Now, every time state.a changes then component A will get re-rendered.

NOTE: you can return anything on your getter function, so for example this is also valid:

const { a, b } = useContextSelection(state => ({ a: state.a, b: state.b });

or even this:

const [ a, b ] = useContextSelection(state => [state.a, state.b]);

Usage

First, you need to create a new Context using the createContext function included in this library (using React.createContext will throw an error).

import { createContext, useContextSelection } from 'use-context-selection';

const AuthContext = createContext({});

Then, you can use Context as you would normally do on your application; here is just an example but you can implement your provider as you want (e.g. using React.useReducer):

const AuthProvider = ({ children }) => {
  const [user, setUser] = React.useState(null);
  const [isLoading, setIsLoading] = React.useState(false);

  function loginFn(username) {
    setIsLoading(true);

    setTimeout(() => {
      setUser({ username });
      setIsLoading(false);
    }, 1000);
  }
  
  const contextValue = {
    user,
    loginFn,
    isLoading,
    isAuthenticated: Boolean(user),
  };

  return (
    <AuthContext.Provider value={contextValue}>{children}</AuthContext.Provider>
  );
}

Now, from you component you can listen for specific parts of the state:

const AppContent = () => {
  const { isLoading, loginFn } = useContextSelection(AuthContext, state => ({
    isLoading: state.isLoading,
    loginFn: state.loginFn,
  }));

  if (isLoading) {
    return 'Loading...';
  }
  return <LoginForm loginFn={loginFn} />;
}

Finally, remember to wrap your application with the Provider.

export default App = () => {
  <AuthProvider>
    <AppContent />
  </AuthProvider>
}

Or you can also use a selection function using Context.Consumer component in this way:

const App = () => (
  <AuthProvider>
    <AuthContext.Consumer selection={state => ({ isLoading: state.isLoading, loginFn: state.loginFn })}>
      {({ isLoading, loginFn }) => {
        if (isLoading) {
          return 'Loading...';
        }
        return <LoginForm loginFn={loginFn} />;
      }}
    </AuthContext.Consumer>
  </AuthProvider>
);

Demo App

Check performance comparison against useContext hook on the following app-example: https://edriang.github.io/use-context-selection/

API

createContext

Creates a smart Context object which compares changes on your Context state and dispatches changes to subscribers.

| Param | Type | Description | Optional / Required | |-------|-------------|-------------|---------------| | initValue | any | Initial value for the Context | Required | | equalityFn | Function | Function used to compare old vs new state; by default it performs shallow equality check | Optional |

  • Return Value: Context

useContextSelection

Hook to access your Context state; receives a Context object created with createContext function and a selection function.

This Hook will trigger a re-render on your component every-time these conditions are met:

  • The state on your Context changes
  • The selection function returns a different value since the last time it rendered

| Param | Type | Description | Optional / Required | |-------|-------------|-------------|---------------| | Context | Context | Context created with createContext function from this library | Required | | selection | Function | Use this selection function to retrieve data from your Context; receives the current state / value and should return whatever your component needs | Required |

  • Return Value: any; whatever you are returning on selection function.

isEqualShallow

This is the default comparator function used internally if equalityFn param is not provided to createContext.

This function is exported as part of the library in case you need it as foundations for your own equality check function.

You need to remember two things about this default equality function:

  • As the name already implies, it performs a shallow equality check for performance reassons;
  • It will ignore comparing functions; this comes handy as you'd probably include in your store functions to mutate the current state; this way there is no need to memoize the functions (e.g. using React.useCallback).

| Param | Type | Description | Optional / Required | |-------|-------------|-------------|---------------| | newState | any | New state to compare with | Required | | oldState | any | Old state to compare with | Required |

  • Return Value: boolean; whether both states are considered the same or not.

Related projects

This library is used internally by react-connect-context-hooks, a library for easily managing application-state.

Disclaimer

This library was inspired by use-context-selector, but here are some key differences:

  • use-context-selection allows you to specify a custom equality-check function.
  • Internally, use-context-selection manages different set of listeners per Context, while use-context-selector stores all listeners (even for different Contexts) within a single shared Set.
  • use-context-selection allows you to use an enhanced version of Context.Consumer component, which supports selection functions like useContextSelection hook.
  • use-context-selector executes different logic when running in production mode than while in development. In production mode it'll trigger updates on listener components during the render phase; this behavior is something React would complain about with some scary warning messages on the console if it runs on development mode.

That being said, based on some internal testing, both libraries are seamlessly performant.

License

MIT © edriang