3.0.4 • Published 1 year ago

@gapu/react-controller v3.0.4

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

@gapu/react-controller

A small, fast and no-boilerplate state-management library for react, using hooks.

npm bundle size MIT License npm (scoped) GitHub Repo stars

Navigation

  1. Introduction
  2. Usage

Introduction

react-controller is a lightweight and user-friendly state management library tailored for React.js applications. Built upon the foundation of immer.js, it simplifies handling nested state management. Designed with versatility and adaptability in mind, this library integrates seamlessly with React.js hooks for a streamlined development experience.

Key Features

  1. No need for context providers: react-controller automatically handles state management without requiring you to set up context providers, making it easier to work with state in your components.
  2. Nested state management: Easily manage and update nested state without worrying about the entire state tree, reducing the complexity of your code and improving performance.
  3. Complex selectors: Retrieve and manipulate multiple parts of the state using complex selectors, enabling more advanced and efficient state handling.
  4. Asynchronous actions support: Handle asynchronous actions directly within your components, making it easier to manage state updates resulting from API calls and other async operations.
  5. External state access: Access and modify your state from outside of React components, giving you more control over your application's state.
  6. Change subscription: Subscribe to state changes and react to updates accordingly, allowing for more complex and dynamic behaviors in your application.
  7. Server-side state initialization: Easily initialize your state on the server side, making it compatible with server-rendered applications like Next.js.
  8. Callback state initialization: Initialize your state with a callback function, allowing for greater flexibility in setting up your initial state.

To get started with react-controller, simply install the package from npm:

npm install @gapu/react-controller

Warning react-controller requires React version >= 16.8.0.

Refer to the provided documentation for detailed usage instructions and examples, and feel free to report any issues or contribute to the development of this library on the GitHub issues page.

Usage

Note Context providers are not needed.

Primitive state

No need for selector callbacks when working with primitive types.

import { create } from '@gapu/react-controller'

const { useSelector, setState } = create(0)

export default function App() {
  const count = useSelector()

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setState((current) => current + 1)}>+ 1</button>
    </div>
  )
}

Nested state

Manage your state in the style of immer.js

import { create } from '@gapu/react-controller'

const { useSelector, setState } = create({ a: { b: [0] } })

const increment = () => {
  setState(current => {
    current.a.b[0] += 1
  })
}

export default function App() {
  const count = useSelector(state => state.a.b[0])

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={increment}>+ 1</button>
    </div>
  )
}

Complex selectors

You can select specific fields out of your state tree, making your selectors much more flexible.

import { create } from '@gapu/react-controller'

const { useSelector } = create({ a: { b: { c: 1 } }, d: { e: { f: 2 } } })

const ObjectSelectorExample = () => {
  const { c, f } = useSelector(state => ({ c: state.a.b.c, f: state.d.e.f }))

  return (
    <div>
      <h1>c: {c}</h1>
      <h1>f: {f}</h1>
    </div>
  )
}

const TupleSelectorExample = () => {
  const [c, e] = useSelector(state => [state.a.b.c, state.d.e])

  return (
    <div>
      <h1>c: {c}</h1>
      <h1>e: {JSON.stringify(e)}</h1>
    </div>
  )
}

export default function App() {
  return (
    <div>
      <ObjectSelectorExample />
      <br />
      <TupleSelectorExample />
    </div>
  )
}

Asynchronous actions

'setState' method supports asynchronous callbacks.

import { create } from '@gapu/react-controller'

const { useSelector, setState } = create({ a: { b: [0] } })

// Mock API call
const getRemoteData = (): Promise<number> => {
  return new Promise(res => {
    setTimeout(() => {
      res(Math.floor(Math.random() * 100))
    }, 1000)
  })
}

const getRandomNumberAsync = () => {
  setState(async current => {
    current.a.b[0] += await getRemoteData()
  })
}

export default function App() {
  const count = useSelector(state => state.a.b[0])

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={getRandomNumberAsync}>random</button>
    </div>
  )
}

External state access

With the help of 'getState' and 'setState' methods, you can access state of your store from outside of react components.

import { create } from '@gapu/react-controller'

const { getState, setState, useSelector } = create({ a: { b: [0] } })

setState(current => {
  current.a.b[0] = 1
}).then(() => {
  console.log(getState().a.b[0]) // 1
})

export default function App() {
  const count = useSelector(state => state.a.b[0])

  return (
    <div>
      {/* <div>1</div> */}
      <h1>{count}</h1>
    </div>
  )
}

Subscribing to changes

When you subscribe to state changes, you get access to next and previous state trees.

import { create } from '@gapu/react-controller'

const { useSelector, setState, subscribe } = create({ a: { b: [0] } })

const unsubscribe = subscribe((nextState, prevState) => {
  console.log(nextState)
  console.log(prevState)
})

const increment = () => {
  setState(current => {
    current.a.b[0] += 1
  })
}

export default function App() {
  const count = useSelector(state => state.a.b[0])

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={increment}>+ 1</button>
    </div>
  )
}

Server-side state initialization (next.js)

Warning Call 'initSS' at the top of page components, before any 'useSelector's

import { create } from '@gapu/react-controller'
import { InferGetServerSidePropsType } from 'next'

const { initSS, useSelector, setState } = create({ count: 0 })

// Mock API call
const getRemoteData = (): Promise<number> => {
  return new Promise(res => {
    setTimeout(() => {
      res(Math.floor(Math.random() * 100))
    }, 100)
  })
}

export const getServerSideProps = async () => {
  return {
    props: {
      count: await getRemoteData()
    }
  }
}

export default function Home(props: InferGetServerSidePropsType<typeof getServerSideProps>) {
  // **IMPORTANT** Call 'initSS' at the top of page components, before any 'useSelector's
  initSS(props)
  const count = useSelector(state => state.count)

  const increment = () => {
    setState((current) => {
      current.count += 1
    })
  }

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={increment}>+ 1</button>
    </div>
  )
}

Callback state initialization

Perhaps you have a persisted state in localStorage which you want to load when the store gets mounted.

import { create } from '@gapu/react-controller'

const { useSelector } = create(() => {
  return { a: { b: [1] } }
})

export default function App() {
  const count = useSelector(state => state.a.b[0])

  return (
    <div>
      {/* <div>1</div> */}
      <h1>{count}</h1>
    </div>
  )
}
1.2.0

1 year ago

2.0.3

1 year ago

2.0.2

1 year ago

2.0.5

1 year ago

2.0.4

1 year ago

2.0.6

1 year ago

2.0.9

1 year ago

2.0.8

1 year ago

2.0.1

1 year ago

2.0.0

1 year ago

3.0.4

1 year ago

3.0.3

1 year ago

3.0.2

1 year ago

3.0.1

1 year ago

3.0.0

1 year ago

2.0.10

1 year ago

1.1.4

1 year ago

1.1.3

1 year ago

1.1.2

1 year ago

1.1.1

1 year ago

1.1.0

1 year ago

1.0.2

1 year ago

1.0.0

1 year ago

0.9.3

1 year ago

0.9.0

1 year ago

0.9.2

1 year ago

0.9.1

1 year ago

0.8.2

1 year ago

0.0.15

1 year ago

0.1.0

1 year ago

0.3.0

1 year ago

0.2.1

1 year ago

0.2.0

1 year ago

0.8.1

1 year ago

0.2.6

1 year ago

0.3.2

1 year ago

0.2.3

1 year ago

0.3.1

1 year ago

0.2.2

1 year ago

0.2.5

1 year ago

0.2.4

1 year ago

0.0.14

1 year ago

0.0.13

1 year ago

0.0.12

1 year ago

0.0.11

1 year ago

0.0.10

1 year ago

0.0.9

1 year ago

0.0.8

1 year ago

0.0.6

1 year ago

0.0.5

1 year ago

0.0.4

1 year ago

0.0.3

1 year ago

0.0.2

1 year ago

0.0.1

1 year ago