A state management library for React, heavily inspired by vuex

Related tags

React react flux state
Overview

Vuex - But for React!

If you know vuex, you know it's as close as we get to a perfect state management library. What if we could do this in the react world?

Installation

npm install vuex-but-for-react --save

yarn add vuex-but-for-react

TS support included

Working example

store.js

const store = {
  state: {
    posts: []
  },
  mutations: {
    POSTS_SET(state, data) {
      state.posts = data
    }
  },
  actions: {
    async POSTS_FETCH(context) {
      const response = await fetch('https://jsonplaceholder.typicode.com/posts')
      const data = await response.json()
      context.mutations.POSTS_SET(data)
    }
  },
  getters: {
    posts (state) {
      return state.posts
    }
  }
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { withStore } from 'vuex-but-for-react';

import App from './App';
import store from './store';

const AppWithStore = withStore(App, store);

ReactDOM.render(
  <AppWithStore />,
  document.getElementById('root')
);

Posts.js

import React, { useEffect } from 'react';
import { useAction, useGetter } from 'vuex-but-for-react';

const Posts = () => {
  const handleAction = useAction('POSTS_FETCH');
  const posts = useGetter('posts');

  useEffect(() => {
    handleAction();
  }, [handleAction]) // don't worry, it doesn't re-create!
  
  return (
    <ul>
      {posts.map(post => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

export default Posts

Check the examples section to see JavaScript and TypeScript working apps!

API

useAction(actionName)

An action is used for async data logic, especially API calls. You can dispatch mutations and other actions from within an action.

The function returned by the useAction() hook is never re-created.

import { useAction } from 'vuex-but-for-react';

const PostsPage = () => {
  const handleFetch = useAction('POSTS_FETCH');

  useEffect(() => {
    handleFetch();
  }, [handleFetch])

  return (
    ...
  )
}

useMutation(actionName)

A mutation is used for sync data operations. It has access to the current state in order to alter it.

The function returned by the useMutation() hook is never re-created.

import { useMutation } from 'vuex-but-for-react';

const Counter = () => {
  const handleIncrement = useMutation('COUNTER_INCREMENT');
  const handleDecrement = useMutation('COUNTER_DECREMENT');

  return (
    <>
      <button onClick={handleDecrement}>-</button>
      <button onClick={handleIncrement}>+</button>
    </>
  )
}

useGetter(actionName)

A getter gives you the current stored value based on your config. It has access to the current state.

The data returned by the useGetter() hook is updated only in case the shallow value changes. An update of one getter value won't trigger the update of another getter value.

import { useGetter } from 'vuex-but-for-react';

const PostsPage = () => {
  const posts = useGetter('posts');

  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

withStore(Component, config)

In order to initialize the global store, wrap your (chosen) root component in your store config.

import { withStore } from 'vuex-but-for-react';

const AppWithStore = withStore(App, store);

And more amazing stuff!

useActionOnMount(actionName)

Avoid calling useEffect manually. Just pass the action name and it will be executed on component mount automatically.

import { useActionOnMount, useGetter } from 'vuex-but-for-react';

const PostsPage = () => {
  useActionOnMount('POSTS_FETCH');
  const posts = useGetter('posts');

  return (
    ...
  )
}

What's going on?

This library uses React's Context API under the hood. The withStore() higher order component is creating several contexts:

  • A context provider for actions, wrapped in a memo() to prevent re-creating
  • A context provider for mutations, wrapped in a memo() to prevent re-creating
  • A context provider for a collection of getter contexts, wrapped in a memo() to prevent re-creating
  • Dynamically created context (and provider) for each one of your getters. This allows us using useGetter() inside a component, which always attaches to its own context. It's an alternative solution to a single-state object context. Updating a getter's provider value will not affect other getters' provider value.
  • Under those contexts, there's the provided App component, wrapped in a memo() to prevent re-creating. Even if the parent context providers change value, App won't re-render and neither will its children, unless they're connected to the getter that was changed.

Tree visualization

Comments
  • Idea: Pass state to actions context

    Idea: Pass state to actions context

    Another idea that would be useful to me is to access the state inside an action context.

    Example:

    // store.js
    const store = {
      state: { count: 0 },
      mutations: {
        SET_COUNT(state, value) {
          state.count = value;
        }
      },
      actions: {
        increment(context) { // context only contains actions and mutations
          context.mutations.SET_COUNT(context.state.count + 1);
        }
      }
    }
    

    Idk if it's possible though, as you mentioned the way the state is written is not really reactive.

    opened by l-portet 1
  • could you please update react dependency?

    could you please update react dependency?

    plugin depends peer react@"^16.8.0" but current react version is now @"17.0.2" could you please fix this!

    Thanks in advance! Very nice Project and I hope I can use it in my Project once updated.

    opened by dwfee 1
  • Pass a full VueX Store Object instead of config?

    Pass a full VueX Store Object instead of config?

    Hi Dana - Thanks for the package, the API looks great and easy to use!

    I'm hoping to introduce this on my project, but long story short, I've got a VueX Store that I need to plug into React Native AND I'm also using Vue Native.

    The Vue Native part can access the VueX Store as normal, has two-way binding, and is completely reactive.

    My issue is, when using withStore(App, config); and instantiating my own Vuex.Store(config); I have two stores! I need to do Vuex.Store() for the Vue part of the application, and the goal is to phase out Vue completely.

    I need to pass withStore the actual Vuex.Store or find another solution to this problem, and was wondering if you have any suggestions??

    Thanks!

    opened by Sunhat 1
  • Idea: Access state without the need of useGetters

    Idea: Access state without the need of useGetters

    First, thanks for the great package :)

    What about a hook to access the state directly instead of having to write a specific getter, such as mapState in VueX? I find it a bit redundant to write getters for each property of my state.

    Example:

    // store.js
    const store = {
      state: { count: 0 },
      mutations: {
        SET_COUNT(state, value) {
          state.count = value;
        }
      },
      actions: {
        increment(context) {
          context.mutations.SET_COUNT(context.state.count + 1);
        }
      }
    }
    
    import { useStoreState } from 'vuex-but-for-react';
    
    export function Counter() {
      const count = useStoreState('count');
      // or const count = useStoreState(state => state.count);
    
      return //...
    }
    
    opened by l-portet 1
  • Can this be used with react-native?

    Can this be used with react-native?

    Hi! This is an awesome package! I would love to use this in my react-native app, but I am not sure if it's supported or how can I change it to use it with react-native projects. Any help is highly appreciated.

    opened by MadalinaCi 2
Owner
Dana Janoskova
JavaScript, TypeScript, Vue.js, React, SASS, Node.JS, PHP, Laravel, Symfony, Photoshop
Dana Janoskova
🐻 Bear necessities for state management in React

A small, fast and scaleable bearbones state-management solution. Has a comfy api based on hooks, isn't boilerplatey or opinionated, but still just eno

Poimandres 25.5k Jan 9, 2023
🏁 High performance subscription-based form state management for React

You build great forms, but do you know HOW users use your forms? Find out with Form Nerd! Professional analytics from the creator of React Final Form.

Final Form 7.2k Jan 7, 2023
Twitter-Clone-Nextjs - Twitter Clone Built With React JS, Next JS, Recoil for State Management and Firebase as Backend

Twitter Clone This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn de

Basudev 0 Feb 7, 2022
Tiny and powerful state management library.

BitAboutState Tiny and powerful React state management library. 100% Idiomatic React. Install npm install --save @bit-about/state Features 100% Idioma

null 53 Nov 5, 2022
Learning how to use redux - a state management library

Redux Learning how to use redux - a state management library What is Redux? Redux is a state management library for JS apps. It centralizes applicatio

Melvin Ng 3 Jul 18, 2022
Create a performant distributed context state by synergyzing atomar context pieces and composing reusable state logic.

Synergies Create a performant distributed context state by synergyzing atomar context pieces and composing reusable state logic. synergies is a tiny (

Lukas Bach 8 Nov 8, 2022
🔮 tiny robust state management

?? snapstate tiny robust state management ?? npm install @chasemoskal/snapstate ??️ watch for changes to properties ??️ track only the properties you

Chase Moskal 5 Dec 23, 2022
A tiny package for JavaScript Web App's state management based on RxJS & Immer

A tiny package for JavaScript Web App's state management based on RxJS & Immer

Xiao Junjiang 12 Oct 19, 2022
A Higher Order Component using react-redux to keep form state in a Redux store

redux-form You build great forms, but do you know HOW users use your forms? Find out with Form Nerd! Professional analytics from the creator of Redux

Redux Form 12.6k Jan 3, 2023
experimental project for babel-plugin-mutable-react-state

Goalist Mutable React Example This example is an attempt to work with babel-plugin-mutable-react-state on a simpler project to see how complicated wou

Reaper 1 Jun 7, 2022
Whoosh - minimalistic React state manager

Whoosh - minimalistic React state manager Whoosh is a React state manager which entire API consists of exactly one function - createShared(). TL;DR ve

Alexander Blk 9 Nov 7, 2022
React Native's Global Alert Component that can be fully customized and without the need of a state.

?? React Native Easy Alert React Native Easy Alert Component. Watch on youtube Easy Alert example app. React Native's Global Alert Component that can

null 9 Feb 21, 2022
Small (0.5 Kb) react hook for getting media breakpoints state info in runtime

tiny-use-media Small (0.5 Kb) react hook for getting media breakpoints state info in runtime Usage npm i tiny-use-media --save Adn in your react code

Valeriy Komlev 51 Dec 13, 2022
Edvora App is a web application based on an external API, showing data about different types of products and the user can filter these data by choosing a specific state, city or product name. Build with React.js

Edvora App is a web application based on an external API, showing data about different types of products and the user can filter these data by choosing a specific state, city or product name. Build with React.js

Kyrillos Hany 5 Mar 11, 2022
Mutated data-store for SolidJS, inspired by React-Redux

solid-mutant Mutated data-store for SolidJS, inspired by React-Redux. Synopsis Redux relies on immutability and referential equality to allow applicat

Lantern 4 Mar 15, 2022
React Query wrapper for NextAuth.js session management

NextAuth.js React-Query Client @next-auth/react-query React Query wrapper for NextAuth.js session management. Overview This is an alternative client f

NextAuth.js 124 Dec 16, 2022
Real state property listing app using next.js , chakra.ui, SCSS

This is a Next.js project bootstrapped with create-next-app. Getting Started First, run the development server: npm run dev # or yarn dev Open http://

null 1 Dec 19, 2021
This hook allows you to isolate and manage the state within the component, reducing rendering operations and keeping the source code concise.

React Hook Component State This hook allows you to isolate and manage the state within the component, reducing rendering operations and keeping the so

Yongwoo Jung 2 May 15, 2022
🖱or ⌨️? 🤷‍♀️, but hopefully use-hover-state works on the "user intent" not the "device"

useHoverState() The one aware of keyboard navigation as well ?? npm i use-hover-state A React hook for tracking user interaction with the DOM elements

Marina 10 Aug 11, 2022