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

redux-react-hook

v4.0.3

Published

React hook for accessing a Redux store.

Downloads

16,840

Readme

redux-react-hook

React hook for accessing mapped state and dispatch from a Redux store.

Build Status NPM Bundle Size Downloads

Table of Contents

Install

# Yarn
yarn add redux-react-hook

# NPM
npm install --save redux-react-hook

Quick Start

//
// Bootstrap your app
//
import {StoreContext} from 'redux-react-hook';

ReactDOM.render(
  <StoreContext.Provider value={store}>
    <App />
  </StoreContext.Provider>,
  document.getElementById('root'),
);
//
// Individual components
//
import {useDispatch, useMappedState} from 'redux-react-hook';
import shallowEqual from 'shallowequal';

export function DeleteButton({index}) {
  // Declare your memoized mapState function
  const mapState = useCallback(
    state => ({
      canDelete: state.todos[index].canDelete,
      name: state.todos[index].name,
    }),
    [index],
  );

  // Get data from and subscribe to the store
  const {canDelete, name} = useMappedState(mapState, shallowEqual);

  // Create actions
  const dispatch = useDispatch();
  const deleteTodo = useCallback(
    () =>
      dispatch({
        type: 'delete todo',
        index,
      }),
    [index],
  );

  return (
    <button disabled={!canDelete} onClick={deleteTodo}>
      Delete {name}
    </button>
  );
}

Usage

NOTE: React hooks require react and react-dom version 16.8.0 or higher.

StoreContext

Before you can use the hook, you must provide your Redux store via StoreContext.Provider:

import {createStore} from 'redux';
import {StoreContext} from 'redux-react-hook';
import reducer from './reducer';

const store = createStore(reducer);

ReactDOM.render(
  <StoreContext.Provider value={store}>
    <App />
  </StoreContext.Provider>,
  document.getElementById('root'),
);

You can also use the StoreContext to access the store directly, which is useful for event handlers that only need more state when they are triggered:

import {useContext} from 'react';
import {StoreContext} from 'redux-react-hook';

function Component() {
  const store = useContext(StoreContext);
  const onClick = useCallback(() => {
    const value = selectExpensiveValue(store.getState());
    alert('Value: ' + value);
  });
  return <div onClick={onClick} />;
}

useMappedState(mapState, equalityCheck?)

Runs the given mapState function against your store state, similar to mapStateToProps. Unlike mapStateToProps, however, the result of your mapState function is compared for reference equality (===) by default. To use shallow equal comparison, pass in a comparision function as the second parameter.

const state = useMappedState(mapState);

You can use props or other component state in your mapState function. It must be memoized with useCallback, because useMappedState will infinitely recurse if you pass in a new mapState function every time.

import {useMappedState} from 'redux-react-hook';

function TodoItem({index}) {
  // Note that we pass the index as a dependency parameter -- this causes
  // useCallback to return the same function every time unless index changes.
  const mapState = useCallback(state => state.todos[index], [index]);
  const todo = useMappedState(mapState);

  return <li>{todo}</li>;
}

If you don't have any inputs (the second argument to useCallback) pass an empty array [] so React uses the same function instance each render. You could also declare mapState outside of the function, but the React team does not recommend it, since the whole point of hooks is to allow you to keep everything in the component.

The second parameter to useMappedState is used to determine if a new result from the mapState function is the same as the previous result, in which case your component will not be re-rendered. Prior to v4.0.1, this was hard-coded to a shallow equality check. Starting in v4.0.1, equalityCheck defaults to reference equality (using ===). To restore the old behavior, which is particularly useful when you are returning an object, you can use the shallowequal module:

import {useMappedState} from 'redux-react-hook';
import shallowEqual from 'shallowequal';

function TodoItem({index}) {
  // Note that we pass the index as a dependency parameter -- this causes
  // useCallback to return the same function every time unless index changes.
  const mapState = useCallback(
    state => ({
      todo: state.todos[index],
      totalCount: state.todos.length,
    }),
    [index],
  );
  const {todo, totalCount} = useMappedState(mapState, shallowEqual);

  return <li>{todo}</li>;
}

To avoid specifying the comparison function on every call to useMappedState, you can provide the defaultEqualityCheck option to create(). The shallowEqual function from fast-equals is another good option, as it handles shallow comparisons of Maps and Sets as well as objects.

NOTE: Every call to useMappedState will subscribe to the store. If the store updates, though, your component will only re-render once. So, calling useMappedState more than once (for example encapsulated inside a custom hook) should not have a large performance impact. If your measurements show a performance impact, you can switch to returning an object instead.

useDispatch()

Simply returns the dispatch method.

import {useDispatch} from 'redux-react-hook';

function DeleteButton({index}) {
  const dispatch = useDispatch();
  const deleteTodo = useCallback(() => dispatch({type: 'delete todo', index}), [
    index,
  ]);

  return <button onClick={deleteTodo}>x</button>;
}

create(options?)

Creates an instance of Redux React Hooks with a new StoreContext. The above functions are just exports of the default instance. You may want to create your own instance if:

  1. You want better type safety without annotating every callsite. Creating your own instance ensures that the types are the same for all consumers. See the example for more info.
  2. You want to provide a default implementation of equalityCheck for all calls to mapState
  3. You have multiple Redux stores (this is not common)
// MyStoreHooks.js

import {create} from 'redux-react-hook';

export const {StoreContext, useDispatch, useMappedState} = create();
// MyStoreHooks.ts

import {create} from 'redux-react-hook';

// Example in TypeScript where you have defined IState and Action
export const {StoreContext, useDispatch, useMappedState} = create<
  IState,
  Action,
  Store<IState, Action>
>();

create takes an optional options object with the following options:

  • defaultEqualityCheck - the default implementation of equalityCheck to use in useMappedState, defaults to refence equality (===)

To restore the pre v4.0.1 comparison behavior, for example:

import {create} from 'redux-react-hook';
import shallowEqual from 'shallow-equal';

// Example in TypeScript where you have defined IState and Action
export const {StoreContext, useDispatch, useMappedState} = create<
  IState,
  Action,
  Store<IState, Action>
>({defaultEqualityCheck: shallowEqual});

Example

You can try out redux-react-hook right in your browser with the Codesandbox example.

To run the example project locally:

# In one terminal, run `yarn start` in the root to rebuild the library itself
cd ./redux-react-example
yarn start

# In another terminal, run `yarn start` in the `example` folder
cd example
yarn start

FAQ

Is it typed with TypeScript/Flow?

One of the nice benefits of using hooks is that they are easier to type and less prone to trouble than higher-order components, especially when you are using multiple hooks (vs multiple HOCs). redux-react-hook comes with both TypeScript definitions and Flow types, both of which should work out of the box when installing with npm/yarn.

How does this compare to React Redux?

redux-react-hook has not been battle and perf-tested, so we don't recommend replacing react-redux just yet. React Redux also guarantees that data flows top down, so that child components update after their parents, which the hook does not.

How do I fix the error "Too many re-renders. React limits the number of renders to prevent an infinite loop."

You're not memoizing the mapState function. Either declare it outside of your stateless functional component or wrap it in useCallback to avoid creating a new function every render.

How can I use a selector creator, like in reselect?

If you want to share a selector with props across multiple component instances, create the selector in useMemo to ensure it has one copy per instance and use it directly in useMappedState.

function TodoList({listID}) {
  // useMemo will execute the function makeGetVisibleTodos once per component
  const getVisibleTodos = useMemo(makeGetVisibleTodos, []);
  const todos = useMappedState(
    useCallback(
      // Note that you shouldn't pass the entire props list, since every time
      // useCallback is recreated, useMappedState will resubscribe
      state => getVisibleTodos(state, {listID}),
      [listID, getVisibleTodos],
    ),
  );
}

Related projects

Here are some other projects that are adding hooks for Redux:

Thanks

Special thanks to @sawyerhood and @sophiebits for writing most of the initial implementation! This repo was setup with the help of the excellent create-react-library.

Contributing

Contributions are definitely welcome! Check out the issues for ideas on where you can contribute. See the CONTRIBUTING.md file for more details.

License

MIT © Facebook Inc.