{"id":19692945,"url":"https://github.com/teefouad/speedux","last_synced_at":"2025-04-29T09:31:34.044Z","repository":{"id":29921913,"uuid":"122091520","full_name":"teefouad/speedux","owner":"teefouad","description":"State management for React with Redux, made easier","archived":false,"fork":false,"pushed_at":"2023-03-05T22:49:10.000Z","size":1323,"stargazers_count":83,"open_issues_count":13,"forks_count":6,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-20T15:45:55.249Z","etag":null,"topics":["asyncronous-actions","flux","javascript","nodejs","react","react-redux","reactjs","redux"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/teefouad.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2018-02-19T16:43:44.000Z","updated_at":"2023-05-22T17:43:44.000Z","dependencies_parsed_at":"2023-09-26T03:54:58.476Z","dependency_job_id":null,"html_url":"https://github.com/teefouad/speedux","commit_stats":{"total_commits":75,"total_committers":1,"mean_commits":75.0,"dds":0.0,"last_synced_commit":"33861d891678cfeffb28e08cb998fb90bd8494e3"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teefouad%2Fspeedux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teefouad%2Fspeedux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teefouad%2Fspeedux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/teefouad%2Fspeedux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/teefouad","download_url":"https://codeload.github.com/teefouad/speedux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251473321,"owners_count":21595038,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncronous-actions","flux","javascript","nodejs","react","react-redux","reactjs","redux"],"created_at":"2024-11-11T19:14:48.840Z","updated_at":"2025-04-29T09:31:33.405Z","avatar_url":"https://github.com/teefouad.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Speedux\n\n[![Version](https://img.shields.io/npm/v/speedux.svg?style=flat-square)](https://www.npmjs.com/package/speedux)\n[![License](https://img.shields.io/npm/l/speedux.svg?style=flat-square)](https://www.npmjs.com/package/speedux)\n[![Downloads](https://img.shields.io/npm/dm/speedux.svg?style=flat-square)](https://www.npmjs.com/package/speedux)\n[![Build Status](https://img.shields.io/travis/teefouad/speedux/master.svg?style=flat-square)](https://travis-ci.org/teefouad/speedux) \n![Coveralls GitHub](https://img.shields.io/coveralls/github/teefouad/speedux.svg)\n\nState management for React with Redux, made easier.\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Contents\n\n- [Installation](#installation)  \n- [Demos](#demos)  \n- [Quick Tutorial](#quick-tutorial)  \n- [Asyncronous Actions](#asyncronous-actions)   \n- [Handling Errors](#handling-errors)  \n- [Listening to Actions](#listening-to-actions)   \n- [Dispatching Actions](#dispatching-actions)   \n- [Updating the State](#updating-the-state)  \n- [Middlewares](#middlewares)  \n- [API](#api)  \n- [The Configuration Object](#the-configuration-object)  \n- [Typescript](#typescript)  \n- [License](#license)\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Installation\n\n**Install with npm**\n```\nnpm install --save speedux\n```\n\n**Install with yarn**\n```\nyarn add speedux\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Demos\n\n[Todos App](https://codesandbox.io/s/speedux-demo-todos-hfkd6)  \n[Shopping Cart App](https://codesandbox.io/s/speedux-demo-shop-1b1ce)\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Quick Tutorial\n\nLet's say you are building a simple counter that displays three buttons. One button increases the count when clicked, another button decreases the count and a third button would reset the count.\n\n### 1. Wrap your app\nStart by importing the `Provider` component from Speedux then wrap your application with it.\n\n```jsx\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'speedux';\n\nimport Counter from './Counter';\n\nconst App = (\n  \u003cProvider\u003e\n    \u003cCounter /\u003e\n  \u003c/Provider\u003e\n);\n\nReactDOM.render(App, document.getElementById('root'));\n```\n\n### 2. Create global state\n\nNext, create the global state that you need to use in your app.\n\n```js\nimport { createGlobalState } from 'speedux';\n\ncreateGlobalState({\n  name: 'counter',\n\n  state: {\n    count: 0,\n  },\n\n  actions: {\n    increaseCount: () =\u003e (prevState) =\u003e ({\n      count: prevState.count + 1,\n    }),\n    decreaseCount: () =\u003e (prevState) =\u003e ({\n      count: prevState.count - 1,\n    }),\n    resetCount: () =\u003e ({\n      count: 0,\n    }),\n  },\n});\n```\n\nThe `createGlobalState` takes a configuration object which describes the global state and how it should be mutated using actions.\n\nEach action (such as `resetCount`) should return an object that describes the changes that should be made to the global state.\n\nIf the global state change depends on the previous global state, return a function instead of an object. For example, consider actions `increaseCount` and `decreaseCount`.\n\n### 3. Use global state in the UI\n\nFinally, you can read the global state and dispatch actions from any component.\n\n```jsx\nimport React from 'react';\nimport { useGlobalState, useActions } from 'speedux'; \n\nconst Counter = () =\u003e {\n  const counterState = useGlobalState('counter');\n  const counterActions = useActions('counter');\n  \n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eCount is: { counterState.count }\u003c/h1\u003e\n\n      \u003cbutton onClick={counterActions.increaseCount}\u003e\n        Increase count\n      \u003c/button\u003e\n\n      \u003cbutton onClick={counterActions.decreaseCount}\u003e\n        Decrease count\n      \u003c/button\u003e\n\n      \u003cbutton onClick={counterActions.resetCount}\u003e\n        Reset count\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default Counter;\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Asyncronous Actions\n\nIn a real world application, you might need to fetch data from a remote source and update the UI accordingly. For such cases, you can use an asyncronous action. To create an asyncronous action, simply use a generator function instead of a normal function.\n\nWhenever your generator function yields an object, that object will be used to [update the component state](#updating-the-state) in the Redux store.\n\nIf your generator function yields a Promise object, the function execution will pause until that promise is resolved and the result will be passed to the generator function on the next call.\n\nHere is an example:\n\n```jsx\ncreateGlobalState({\n  name: 'dataFetcher',\n\n  state: {\n    loading: false,\n    data: null,\n  },\n\n  actions: {\n    * fetchData() {\n      // Yield an object to update the state and indicate that\n      // the data is being loaded. You can use `props.state.loading`\n      // to display a spinner or something similar.\n      yield { loading: true };\n    \n      // Yield a promise to fetch the data\n      const response = yield fetch('/api/posts');\n      // `fetch` resolves to a promise that needs to be resolved to json\n      const data = yield response.json();\n      \n      // Finally, yield an object to populate the state with the\n      // loaded data and indicate that the data has been loaded\n      yield {\n        loading: false,\n        data,\n      };\n    },\n  },\n});\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Handling Errors\n\nTo handle errors in an asyncronous action, you can use `.catch()` or you can check if the resolved response is an instance of `Error`:\n\n```jsx\ncreateGlobalState({\n  name: 'faultyDataFetcher',\n\n  state: {\n    loading: false,\n    data: null,\n    error: null,\n  },\n\n  actions: {\n    /* Handle errors using `.catch()` */\n    * fetchData() {\n      // Yield an object to update the state and indicate that\n      // the data is being loaded. You can use `props.state.loading`\n      // to display a spinner or something similar.\n      yield { loading: true };\n      \n      // Yield a promise to fetch the data\n      const response = yield fetch('/api/posts').catch(e =\u003e {\n        console.log('Failed to fetch data');\n\n        // Optionally return a fallback value\n        return { failed: true, posts: [] };\n      });\n      \n      // Handle loading errors\n      if (response.failed === true) {\n        ...\n      } else {\n        ...\n      }\n    },\n\n    /* Handle errors using `instanceof` */\n    * fetchOtherData() {\n      // Yield an object to update the state and indicate that\n      // the data is being loaded.\n      yield { loading: true };\n      \n      // Yield a promise to fetch the data\n      const response = yield fetch('/api/posts');\n      \n      // Handle loading errors\n      if (response instanceof Error) {\n        yield { error: response.message };\n      } else {\n        ...\n      }\n    },\n  },\n});\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Listening to Actions\n\nYou can use the [`handlers`](#handlers-object) configuration option to listen to any action dispatched by the Redux store.\n\nSimply, use the action type as the key and the handler function as the value. The handler function will always receive the action object as a single parameter and should return an object that specifies the [state keys that need to be updated](#updating-the-state) and their new values.\n\nHere is an example:\n\n```jsx\ncreateGlobalState({\n  name: 'routerSpy',\n\n  state: { currentPath: null },\n\n  handlers: {\n    '@@router/LOCATION_CHANGE': (action) =\u003e {\n      return {\n        currentPath: action.payload.location.pathname,\n      };\n    },\n  },\n});\n```\n\nYou can also listen to [actions](#actions-object) that were defined in a [`configuration object`](#the-configuration-object).\n\nFor example, if we have a global state `foo`:\n\n```jsx\ncreateGlobalState({\n  name: 'foo',\n\n  actions: {\n    saySomething(message) { ... }\n  },\n  ...\n});\n```\n\nAnd another global state `baz` that needs to listen to action `saySomething` which is defined in `foo`:\n\n```jsx\ncreateGlobalState({\n  name: 'baz',\n\n  state: {\n    text: null,\n  },\n\n  handlers: {\n    'foo.saySomething': function(action) {\n      return {\n        text: `Foo said: ${action.payload.message}!`\n      };\n    },\n  },\n  ...\n});\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Dispatching Actions\n\nYou can use the `useDispatch` hook to create a dispatch function.\n\nHere is an example:\n\n```jsx\nimport { useDispatch } from 'speedux';\n\nconst MyComponent = () =\u003e {\n  const dispatch = useDispatch();\n\n  // Dispatches an action with type 'something'\n  function onClickButton() {\n    dispatch({\n      type: 'someAction',\n      payload: {\n        value: 'abc',\n      },\n    });\n  }\n  \n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={onClickButton}\u003e\n        Dispatch an action\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\nYou can also dispatch [actions](#actions-object) that were defined in a [configuration object](#the-configuration-object).\n\nFor example, let's say that we have a global state `profile` that displays the availability of a user:\n\n```jsx\ncreateGlobalState({\n  name: 'profile',\n\n  state: {\n    userStatus: 'online',  \n  },\n\n  actions: {\n    setUserStatus(userStatus) {\n      return { userStatus };\n    },\n  },\n  ...\n});\n```\n\nAnd you need to dispatch `setUserStatus` action from component `Baz`:\n\n```jsx\nimport { useDispatch } from 'speedux';\n\nconst Baz = () =\u003e {\n  const dispatch = useDispatch();\n\n  function setStatus(status) {\n    dispatch('profile.setUserStatus', status);\n  }\n  \n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={() =\u003e setStatus('online')}\u003e\n        Appear Online\n      \u003c/button\u003e\n      \n      \u003cbutton onClick={() =\u003e setStatus('offline')}\u003e\n        Appear Offline\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Updating the State\n\nBoth [action](#actions-object) and [handler](#handlers-object) functions define how the state should be updated by returning an object. This object specifies the state keys that need to be updated and their new values. In the following example, `changeFirstName` will only update `firstName` in the state with value `Bingo` while `lastName` will remain the same.\n\n```jsx\ncreateGlobalState({\n  name: 'foo',\n\n  state: {\n    firstName: 'baz',\n    lastName: 'boo',\n  },\n\n  actions: {\n    changeFirstName() {\n      return { firstName: 'Bingo' };\n    }\n  }\n});\n\nconst MyComponent = () =\u003e {\n  const state = useGlobalState('foo');\n  const actions = useActions('foo');\n\n  // Before clicking the button: { firstName: 'baz', lastName: 'boo' }\n  // After clicking the button: { firstName: 'Bingo', lastName: 'boo' }\n  console.log(state);\n  \n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={actions.changeFirstName}\u003e\n        Click me\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### Nested State Keys\nTo update deeply nested state keys, you can use dot notation as a string:\n\n```jsx\ncreateGlobalState({\n  name: 'myComponent',\n\n  state: {\n    data: {\n      list: [\n        { props: { name: 'feeb' } },\n        { props: { name: 'foo' } },\n        { props: { name: 'fiz' } },\n      ],\n    },\n  },\n  \n  actions: {\n    changeFooName(newName) {\n      return { 'data.list[1].props.name': newName };\n    },\n  },\n});\n```\n\n### Wildcard Character: *\nIf you would like to modify all items of an array or an object in the state, use a wildcard character:\n\n```jsx\ncreateGlobalState({\n  name: 'foo',\n\n  state: {\n    list: [\n      { name: 'feeb' },\n      { name: 'foo' },\n      { name: 'fiz' },\n    ],\n  },\n  \n  actions: {\n    changeAllNames(newName) {\n      return { 'list.*.name': newName };\n    },\n  },\n});\n\n/*\nInvoking action changeAllNames('jane') will modify the state to:\n{\n  list: [\n    { name: 'jane' },\n    { name: 'jane' },\n    { name: 'jane' },\n  ],\n}\n*/\n```\n\n### Mapper Function\n\nIf you need to dynamically calculate the new value of the state key based on the old value, return a function instead of an object:\n\n```jsx\ncreateGlobalState({\n  name: 'counter',\n\n  state: {\n    count: 0,\n  },\n  \n  actions: {\n    increaseCount: () =\u003e (prevState) =\u003e ({\n      count: prevState.count + 1,\n    }),\n  },\n});\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Middlewares\n\nTo use a middleware, import [`useMiddleware`](#usemiddlewaremiddleware) method and pass it the middleware function. You don't need to use `applyMiddleware` from Redux, this method will be called internally by Speedux.\n\nHere is an example using React Router DOM (v5.1.2) and Connected React Router (v6.6.1):\n\n```jsx\nimport { Provider, useReducer, useMiddleware } from 'speedux';\nimport { ConnectedRouter, connectRouter, routerMiddleware } from 'connected-react-router';\nimport { createBrowserHistory } from 'history';\n\nconst history = createBrowserHistory();\n\n// connected-react-router requires its reducer to be mounted under 'router'\nuseReducer('router', connectRouter(history));\nuseMiddleware(routerMiddleware(history));\n\nReactDOM.render((\n  \u003cProvider\u003e\n    \u003cConnectedRouter history={history}\u003e\n      ...\n    \u003c/ConnectedRouter\u003e\n  \u003c/Provider\u003e\n), document.getElementById('root'));\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# API\n\n### createGlobalState(configuration)\n\n| Parameter | Type | Description |\n| :----- | :----- | :----- |\n| configuration | Object | The [configuration object](#the-configuration-object) for the global state. |\n\nThe `createGlobalState` function creates a global state in the Redux store using the specified configuration object.\n\n##### Example:\n```jsx\nimport { createGlobalState } from 'speedux';\n\ncreateGlobalState({\n  name: 'foo',\n\n  state: {\n    value: 'abc',\n  },\n\n  actions: {\n    setValue(newValue) {\n      return { value: newValue };\n    },\n  },\n});\n```\n\n\u0026nbsp;\n\n### useGlobalState(name)\n\n| Parameter | Type | Description |\n| :----- | :----- | :----- |\n| name | String | Name of the global state to retrieve. |\n\nOnce you have created a global state using `createGlobalState`, you can use `useGlobalState` hook in any component to retrieve that global state.\n\n##### Example:\n```jsx\nimport { useGlobalState } from 'speedux';\n\nconst MyComponent = () =\u003e {\n  const state = useGlobalState('foo');\n  return (...);\n};\n```\n\n\u0026nbsp;\n\n### useActions(name)\n\n| Parameter | Type | Description |\n| :----- | :----- | :----- |\n| name | String | Name of the global state to retrieve its actions. |\n\nOnce you have created a global state using `createGlobalState` and defined actions that mutate that global state, you can use `useActions` hook in any component to retrieve those actions.\n\n##### Example:\n```jsx\nimport { useActions } from 'speedux';\n\nconst MyComponent = () =\u003e {\n  const actions = useActions('foo');\n  return (\n    \u003cbutton onClick={() =\u003e actions.setValue('Hello world!')}\u003e\n      Click to set value\n    \u003c/button\u003e\n  );\n};\n```\n\n\u0026nbsp;\n\n### useDispatch()\n\nYou can use the `useDispatch` hook to create a [dispatch function](#dispatching-actions).\n\n##### Example:\n```jsx\nimport { useDispatch } from 'speedux';\n\nconst MyComponent = () =\u003e {\n  const dispatch = useDispatch();\n  return (\n    \u003cbutton onClick={() =\u003e dispatch({ type: 'SOME_ACTION' })}\u003e\n      Click to dispatch an action\n    \u003c/button\u003e\n  );\n};\n```\n\n\u0026nbsp;\n\n### useHandler(actionType, callback)\n\n| Parameter | Type | Description |\n| :----- | :----- | :----- |\n| actionType | String | Type of the action to listen to. |\n| callback | Function | Function to be called when that action is dispatched. |\n\nYou can use the `useHandler` hook to listen to any action dispatched by the Redux store.\n\n##### Example:\n```jsx\nimport { useHandler } from 'speedux';\n\nconst MyComponent = () =\u003e {\n  useHandler('@@redux/INIT', () =\u003e {\n    console.log('INIT action has been dispatched');\n  });\n  return (...);\n};\n```\n\n\u0026nbsp;\n\n### useReducer(key, reducer)\n\nAllows registering a reducer function that can listen to any action dispatched by the store and modify the global state accordingly.\n\n| Parameter | Type | Description |\n| :----- | :----- | :----- |\n| key | String | A unique identifier key for the reducer. |\n| reducer | Function | Reducer function to use. |\n\n##### Example:\n```jsx\nimport { useReducer } from 'speedux';\nimport { connectRouter } from 'connected-react-router';\nimport { createBrowserHistory } from 'history';\n\nconst history = createBrowserHistory();\nconst routerReducer = connectRouter(history);\n\nuseReducer('router', routerReducer);\n```\n\n\u0026nbsp;\n\n### useMiddleware(middleware)\n\nAllows using middleware functions such as React Router middleware and others. You don't need to use `applyMiddleware` from Redux before passing the middleware to this function.\n\n| Parameter | Type | Description |\n| :----- | :----- | :----- |\n| middleware | Function | Middleware function to use. |\n\n##### Example:\n```jsx\nimport { useMiddleware } from 'speedux';\nimport { routerMiddleware } from 'connected-react-router';\nimport { createBrowserHistory } from 'history';\n\nconst history = createBrowserHistory();\n\nuseMiddleware(routerMiddleware(history));\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# The Configuration Object\n\nThe configuration object may contain one or more of the following keys:\n\n### name (String) - required\n\nThe `name` key is the only required key in the configuration object. It *must* be unique for each global state as it is used to identify the global state in the Redux store.\n\n### state (Object)\nRepresents the piece of global state (or initial state) in the Redux store. If not provided, an empty object will be used.\n\n### actions (Object)\nA list of all the actions that may need to be dispatched from the UI to update the state. Provide the action name as the key and the function as the value.\n\nThe key or function name will be used to generate the action type. For example, a name `calculator` with a defined action `addNumbers` will dispatch an action of type `@@calculator/ADD_NUMBERS` whenever `addNumbers()` is called.\n\nThe function should return an object that specifies the state keys that need to be updated and their new values.\n\n```jsx\ncreateGlobalState({\n  name: 'calculator',\n\n  state: {\n    result: 0,\n  },\n\n  actions: {\n    addNumbers(x, y) {\n      return { result: x + y };\n    }\n  }\n});\n```\n\nTo create an asyncronous action, simply use a generator function instead of a normal function.\n\nWhenever your generator function yields an object, that object will be used to update the component state in the Redux store.\n\nIf your generator function yields a Promise object, the function execution will pause until that promise is resolved and the result will be passed to the generator function on the next call.\n\nSee [Asyncronous Actions](#asyncronous-actions) for examples.\n\n### handlers (Object)\nA list of all the external actions that may affect the global state. Provide the action type as the key and the handler function as the value. You can listen to any action dispatched by the Redux store.\n\nThe handler function will always receive the action object as a single parameter and should return an object that specifies the state keys that need to be updated and their new values.\n\nSee [Listening to Actions](#listening-to-actions) for examples.\n\n\u0026nbsp;\n\u0026nbsp;\n\n# Typescript\n\nInside `counter-state.ts`:\n\n```js\nimport { createGlobalState } from 'speedux';\n\nexport interface CounterState {\n  count: number;\n}\n\nexport interface CounterActions {\n  increaseCount: () =\u003e void;\n  decreaseCount: () =\u003e void;\n  setCount: (value: number) =\u003e void;\n  fetchCount: () =\u003e Promise\u003cvoid\u003e;\n}\n\nexport default createGlobalState\u003cCounterState, CounterActions\u003e({\n  name: 'counter',\n\n  state: {\n    count: 0,\n  },\n\n  actions: {\n    increaseCount: () =\u003e (prevState) =\u003e ({\n      count: prevState.count + 1,\n    }),\n    decreaseCount: () =\u003e (prevState) =\u003e ({\n      count: prevState.count - 1,\n    }),\n    setCount: (value) =\u003e ({\n      count: value,\n    }),\n    fetchData: function* () {\n      // Async action\n      ...\n    },\n  },\n});\n```\n\nInside the component file:\n\n```jsx\nimport React from 'react';\nimport { useGlobalState, useActions } from 'speedux';\nimport { CounterState, CounterActions } from './counter-state';\n\nconst Counter = () =\u003e {\n  const counterState = useGlobalState\u003cCounterState\u003e('counter');\n  const counterActions = useActions\u003cCounterActions\u003e('counter');\n  \n  return (\n    \u003cdiv\u003e\n      ...\n    \u003c/div\u003e\n  );\n};\n\nexport default Counter;\n```\n\nAlternatively, you can do this:\n\n```jsx\nimport React from 'react';\nimport counterStateManager from './counter-state';\n\nconst Counter = () =\u003e {\n  const counterState = counterStateManager.useState();\n  const counterActions = counterStateManager.useActions();\n  \n  return (\n    \u003cdiv\u003e\n      ...\n    \u003c/div\u003e\n  );\n};\n\nexport default Counter;\n```\n\n\u0026nbsp;\n\u0026nbsp;\n\n# License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteefouad%2Fspeedux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fteefouad%2Fspeedux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fteefouad%2Fspeedux/lists"}