kea

repository·master·Indexed 24 days ago

https://github.com/keajs/kea

A state management library for handling complex application logic and state transitions. Kea provides a smart front-end architecture featuring a core plugin system for actions, reducers, selectors, and listeners, as well as React hooks like useValues, useActions, and useKea for lifecycle management. It supports keyed logic, a defaults API for initial state, and can be used in non-React environments via manual mounting.

Tokens
8.2K
Snippets
5
Records
57
Agent score
83%

What's inside kea

  1. Configure Kea context and plugins with resetContext()

    master

    In Kea 1.0+, all plugins and configuration options must be defined on the context rather than passed to individual kea() calls. The recommended way to initialize your application is to call resetContext() at a high level in your app. This is particularly useful for server-side rendering as it allows you to clear the cache for each render.

    To set up your application:

    1. Call resetContext() with your desired plugins and options.
    2. Retrieve the store from the context using getContext().
    3. Pass that store to your React-Redux <Provider>.
    // 1. Initialize context with plugins and options
    resetContext({
      plugins: [sagaPlugin, localStoragePlugin],
      // other options like defaults, plugin config, redux strategy, etc.
    });
    
    // 2. In your App component, get the store and provide it
    function App ({ children }) {
      const { store } = getContext()
      return (
        <Provider store={store}>
          {children}
        </Provider>
      )
    }
  2. Upgrade to Kea 1.0+

    master

    To upgrade to Kea 1.0+, ensure your environment meets the following dependency requirements:

    • react-redux: version 7.1 or later
    • react: version 16.8.3 or later

    Then, upgrade all related Kea packages to their latest 1.0+ versions:

    • kea
    • kea-saga
    • kea-thunk
    • kea-localstorage
  3. Understand the Logic interface

    master

    The Logic interface is the fundamental building block of Kea. It represents a self-contained unit of state, actions, and selectors. A Logic object contains:

    • Identity: path, pathString, and key (for keyed logic).
    • Actions: actionCreators, actionKeys, actionTypes, actions, and asyncActions.
    • State Management: defaults, reducers, and selector (to derive state).
    • Selectors: selectors (a record of functions to extract specific pieces of state).
    • Lifecycle Events: events (e.g., beforeMount, afterMount, propsChanged).
    • Listeners: listeners and sharedListeners for reacting to actions.
  4. How selectors and prop selectors work together

    master

    In Kea, selectors are designed to be reactive to both the global state and the local logic props. When defining a selector via selectors(), the input function provides two specialized objects:

    1. selectors: Access to other selectors defined within the same logic. This allows for composing selectors.
    2. propSelectors: A proxy-like object used to access the logic's props. Instead of accessing props.id directly, you use the propSelectors to return a function that retrieves the prop. This ensures the selector remains memoized and correctly tracks prop changes.

    Example Pattern:

    selectors({
      // A base selector accessing state
      duckId: [(s) => [s.duckId], (id) => id],
      
      // A composed selector using another selector and a prop
      duckAndChicken: [
        (s, p) => [s.duckId, p.id], // s.duckId is the selector above, p.id is a prop
        (duckId, id) => duckId + id
      ]
    })
  5. Use BuiltLogic and LogicWrapper for mounting and extending

    master

    When you define a logic, Kea returns a BuiltLogic or a LogicWrapper. These objects provide methods to manage the logic's lifecycle and extend its functionality:

    • mount(): Returns an unmount function. Used to start the logic.
    • unmount(): Stops the logic.
    • isMounted(keyOrProps?): Checks if a specific instance of the logic is currently mounted.
    • find(keyOrProps?): Returns a mounted logic instance or throws if not found.
    • findMounted(keyOrProps?): Returns a mounted logic instance or null.
    • extend(extendedInput): Creates a new LogicWrapper by extending the current logic with new inputs (actions, reducers, etc.).
    • wrap(Component): Wraps a React component to connect it to the logic, returning a KeaComponent.
  6. How reducers() and selectors work together

    master

    In Kea, state management is tightly coupled. When you define a key in reducers(), Kea performs several side effects to ensure the state is accessible:

    1. Path Selection: It uses rootSelector() to create a base selector that points to the logic's specific slice of the Redux state using pathSelector.
    2. Selector Creation: For every key in your reducers definition, Kea uses addSelectorAndValue to create a memoized selector (via reselect) that extracts that specific key from the logic's state slice.
    3. Default Values: If no initial value is provided in the array, Kea attempts to resolve a default value from getContextDefaults or a global root default selector (logic.defaults['*']).

    This ensures that as soon as you define a reducer, you immediately have a corresponding selector available to read that state.

  7. How pauseListenersEnhancer works

    master
    The pauseListenersEnhancer is a built-in Redux store enhancer used by Kea to prevent unnecessary re-renders. It intercepts Redux subscribe calls and checks if the application is currently in a 'paused' state (using isPaused()). If the application is paused (typically during logic mounting), the observer is prevented from executing, which avoids early React re-renders caused by state changes during the initialization phase.
  8. Implement Listeners with Breakpoints

    master

    A ListenerFunction allows you to react to actions. It receives the action's payload, the action itself, the previous state, and a BreakPointFunction.

    BreakPointFunction is a special function that can be used to pause or delay execution. It can be called without arguments to return immediately, or with a millisecond value (ms: number) => Promise<void> to create a delay.

    // Example signature of a listener
    type ListenerFunction<A extends AnyAction = any> = (
      payload: A['payload'],
      breakpoint: BreakPointFunction,
      action: A,
      previousState: any,
    ) => void | Promise<void>;
  9. Initialize and manage the Kea context

    master

    The Kea context is the central registry for plugins, stores, and logic. You manage its lifecycle using openContext, closeContext, and resetContext.

    • openContext(options, initial): Creates and activates a new context. If a context is already open, it will log an error to the console. By default, it automatically creates a Redux store unless createStore: false is passed in the options.
    • closeContext(): Tears down the current context and triggers beforeCloseContext plugin events.
    • resetContext(options, initial): A convenience method that closes the existing context and opens a new one with the provided options.
    • getContext(): Retrieves the currently active context.

    Note on Store Creation: The Redux store is lazily initialized. It is created the first time context.store is accessed, unless you provide a custom store via options.createStore or set createStore: false.

  10. Use Kea hooks: useValues, useActions, and useAllValues

    master

    Kea provides several React hooks to interact with logic.

    • useValues(logic): Returns the state of the logic's values. Note: This hook returns getters that call useSelector under the hood. You must directly destructure the returned object (e.g., const { name } = useValues(logic)) and not store the whole object in a variable to use later.
    • useAllValues(logic): Use this if you need to store the entire values object in a variable for later use. This is the safe alternative to storing the result of useValues.
    • useActions(logic): Returns the actions defined in the logic.

    Logic is automatically mounted and unmounted when using these hooks in a component.

    function NameComponent () {
      const { name } = useValues(logic) // Destructure directly
      const { updateName } = useActions(logic)
    
      return (
        <div>
          <div>Name: {name}</div>
          <button onClick={() => updateName('George')}>Change</button>
        </div>
      )
    }
  11. Use Kea without React

    master

    If you have a configured Kea context and a connected Redux store, you can use Kea in non-React environments by manually mounting logic.

    For non-keyed logic, use logic.mount(). For keyed logic, you must first build it with props using logic(props), then call .mount() on the resulting object. mount() returns an unmount function to clean up the logic and disconnect it from the store.