Coder Social home page Coder Social logo

useeffectreducer's Introduction

useEffectReducer

A React hook for managing side-effects in your reducers.

Inspired by the useReducerWithEmitEffect hook idea by Sophie Alpert.

If you know how to useReducer, you already know how to useEffectReducer.

๐Ÿ’ป CodeSandbox example: Dog Fetcher with useEffectReducer

Installation

Install it:

npm install use-effect-reducer

Import it:

import { useEffectReducer } from 'use-effect-reducer';

Create an effect reducer:

const someEffectReducer = (state, event, exec) => {
  // execute effects like this:
  exec(() => {/* ... */});

  // or parameterized (better):
  exec({ type: 'fetchUser', user: event.user });

  // and treat this like a normal reducer!
  // ...

  return state;
});

Use it:

// ...
const [state, dispatch] = useEffectReducer(someEffectReducer, initialState, {
  // implementation of effects
});

// Just like useReducer:
dispatch({ type: 'FETCH', user: 'Sophie' });

Quick Start

An "effect reducer" takes 3 arguments:

  1. state - the current state
  2. event - the event that was dispatched to the reducer
  3. exec - a function that captures effects to be executed.
import { useEffectReducer } from 'use-effect-reducer';

// I know, I know, yet another counter example
const countReducer = (state, event, exec) => {
  switch (event.type) {
    case 'INC':
      exec(() => {
        // "Execute" a side-effect here
        console.log('Going up!');
      });

      return {
        ...state,
        count: state.count + 1,
      };

    default:
      return state;
  }
};

const App = () => {
  const [state, dispatch] = useEffectReducer(countReducer, { count: 0 });

  return <div>
    <output>Count: {state.count}</output>
    <button onClick={() => dispatch('INC')}>
      Increment
    </button>
  </div>;
};

Named Effects

A better way to make reusable effect reducers is to have effects that are named and parameterized. This is done by running exec(...) an effect object (instead of a function) and specifying that named effect's implementation as the 3rd argument to useEffectReducer(reducer, initial, effectMap).

const fetchEffectReducer = (state, event, exec) => {
  switch (event.type) {
    case 'FETCH':
      // Capture a named effect to be executed
      exec({ type: 'fetchFromAPI', user: event.user });

      return {
        ...state,
        status: 'fetching',
      };
    case 'RESOLVE':
      return {
        status: 'fulfilled',
        user: event.data,
      };
    default:
      return state;
  }
};

const Fetcher = () => {
  const [state, dispatch] = useEffectReducer(
    fetchEffectReducer,
    { status: 'idle', user: undefined },
    {
      // Specify how effects are implemented
      fetchFromAPI: (_, effect) => {
        fetch(`/api/users/${effect.user}`)
          .then(res => res.json())
          .then(data => {
            dispatch({
              type: 'RESOLVE',
              data,
            });
          });
      },
    }
  );

  return (
    <button
      onClick={() => {
        dispatch({ type: 'FETCH', user: 42 });
      }}
    >
      Fetch user
    </div>
  );
};

API

useEffectReducer hook

The useEffectReducer hook takes the same first 2 arguments as the built-in useReducer hook, and returns the current state returned from the effect reducer, as well as a dispatch function for sending events to the reducer.

const SomeComponent = () => {
  const [state, dispatch] = useEffectReducer(someEffectReducer, initialState);

  // ...
};

Additionally, the useEffectReducer hook takes a 3rd argument, which is the implementation details for named effects:

const SomeComponent = () => {
  const [state, dispatch] = useEffectReducer(someEffectReducer, initialState, {
    log: (state, effect) => {
      console.log(state);
    },
  });

  // ...
};

TypeScript

The effect reducer can be specified as an EffectReducer<TState, TEvent, TEffect>, where the generic types are:

  • The state type returned from the reducer
  • The event object type that can be dispatched to the reducer
  • The effect object type that can be executed
import { useEffectReducer, EffectReducer } from 'use-effect-reducer';

interface User {
  name: string;
}

type FetchState =
  | {
      status: 'idle';
      user: undefined;
    }
  | {
      status: 'fetching';
      user: User | undefined;
    }
  | {
      status: 'fulfilled';
      user: User;
    };

type FetchEvent =
  | {
      type: 'FETCH';
      user: string;
    }
  | {
      type: 'RESOLVE';
      data: User;
    };

type FetchEffect = {
  type: 'fetchFromAPI';
  user: string;
};

const fetchEffectReducer: EffectReducer<FetchState, FetchEvent, FetchEffect> = (
  state,
  event,
  exec
) => {
  switch (event.type) {
    case 'FETCH':
    // State, event, and effect types will be inferred!

    // Also you should probably switch on
    // `state.status` first ;-)

    // ...

    default:
      return state;
  }
};

useeffectreducer's People

Contributors

davidkpiano avatar

Watchers

James Cloos 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.