Code Monkey home page Code Monkey logo

redux-react-hook's Introduction

redux-react-hook

React hook for accessing mapped state from a Redux store. Basically a hooks version of react-redux.

NPM

Install

# Yarn
yarn add redux-react-hook

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

Usage

All the hooks take a storeContext as the first parameter, which should be a context object, the value returned by React.createContext(...), that contains your Redux store. See Custom Wrappers below to make this less cumbersome.

Store in Context

Before you can use the hook, you must put your Redux store into Context:

// Store.js

import React from 'react';
import {createStore} from 'redux';
import reducer from './reducer';

export function makeStore() {
  return createStore(reducer);
}

export const Context = React.createContext(null);
// index.js

import {Context, makeStore} from './Store';

const store = makeStore();

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

useMappedState(storeContext, mapState)

Runs the given mapState function against your store state, just like mapStateToProps.

const state = useMappedState(storeContext, mapState);

If your mapState function doesn't use props or other component state, declare it outside of your stateless functional component:

import {useMappedState} from 'redux-react-hook';
import {Context} from './Store';

// Note how mapState is declared outside of the function -- this is critical, as
// useMappedState will infinitely recurse if you pass in a new mapState
// function every time.
const mapState = state => ({
  lastUpdated: state.lastUpdated,
  todoCount: state.todos.length,
});

export default function TodoSummary() {
  const {lastUpdated, todoCount} = useMappedState(Context, mapState);
  return (
    <div>
      <div>Count: {todoCount}</div>
      <div>Last updated: {new Date(lastUpdated).toString()}</div>
    </div>
  );
}

If you need to use props or other component state in your mapState function, memoize the function with useCallback:

import {useMappedState} from 'redux-react-hook';
import {Context} from './Store';

function TodoItem({index}) {
  // Note that we pass the index as a memoization 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(storeContext, mapState);

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

useDispatch(storeContext)

Simply returns the dispatch method.

import {useMappedState} from 'redux-react-hook';
import {Context} from './Store';

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

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

Custom wrappers

To avoid having to pass in a storeContext with every call, we recommend adding project specific wrappers for useMappedState and useDispatch:

// Store.js

import React from 'react';
import {
  useDispatch as useDispatchGeneric,
  useMappedState as useMappedStateGeneric,
} from 'redux-react-hook';

export const Context = React.createContext(null);

export function useMappedState(mapState) {
  return useMappedStateGeneric(Context, mapState);
}

export function useDispatch() {
  return useDispatchGeneric(Context);
}

The useMappedState wrapper is also an ideal place to restrict the store state that you want passed to mapState. For example, if your store schema has an undo stack, and you only want to pass the current state.

export function useMappedState(mapState) {
  const mapRestrictedState = useCallback(
    fullState => mapState(fullState.currentState),
    [mapState],
  );
  return useMappedStateGeneric(Context, mapRestrictedState);
}

See the example project for the full code.

Example

To run the example project, a simple todo app:

cd example
yarn start

FAQ

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.

Contributing

Contributions are definitely welcome! Check out the issues for ideas on where you can contribute.

License

MIT © ianobermiller

redux-react-hook's People

Contributors

ianobermiller avatar

Watchers

agnes avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.