-
-
Notifications
You must be signed in to change notification settings - Fork 9.9k
State Management Selectors
Selectors are data retrieval modules under the LobeChat data flow development framework. Their role is to extract data from the store using specific business logic for consumption by components.
Taking src/store/plugin/selectors.ts
as an example:
This TypeScript code snippet defines an object named pluginSelectors
, which contains a series of selector functions used to retrieve data from the plugin storage state. Selectors are functions that extract and derive data from a Redux store (or similar state management library). This specific example is for managing the state related to the frontend application's plugin system.
Here are some key points to note:
-
enabledSchema
: A function that returns an array ofChatCompletionFunctions
filtered based on the enabled plugin listenabledPlugins
. It appends the plugin identifier as a prefix to the API names to ensure uniqueness and uses theuniqBy
function from the Lodash library to remove duplicates. -
onlinePluginStore
: Returns the current online plugin list. -
pluginList
: Returns the list of plugins, including custom plugins and standard plugins. -
getPluginMetaById
: Returns the plugin metadata based on the plugin ID. -
getDevPluginById
: Returns information about the custom plugins in development. -
getPluginManifestById
: Returns the plugin manifest based on the plugin ID. -
getPluginSettingsById
: Returns the plugin settings based on the plugin ID. -
getPluginManifestLoadingStatus
: Returns the loading status of the plugin manifest (loading, success, or error) based on the plugin ID. -
isCustomPlugin
: Checks if the plugin with the given ID is a custom plugin. -
displayPluginList
: Returns a processed plugin list, including author, avatar, creation time, description, homepage URL, identifier, and title. -
hasPluginUI
: Determines if the plugin has UI components based on the plugin ID.
Selectors are highly modular and maintainable. By encapsulating complex state selection logic in separate functions, they make the code more concise and intuitive when accessing state data in other parts of the application. Additionally, by using TypeScript, each function can have clear input and output types, which helps improve code reliability and development efficiency.
Taking the displayPluginList
method as an example, its code is as follows:
const pluginList = (s: PluginStoreState) => [...s.pluginList, ...s.customPluginList];
const displayPluginList = (s: PluginStoreState) =>
pluginList(s).map((p) => ({
author: p.author,
avatar: p.meta?.avatar,
createAt: p.createAt,
desc: pluginHelpers.getPluginDesc(p.meta),
homepage: p.homepage,
identifier: p.identifier,
title: pluginHelpers.getPluginTitle(p.meta),
}));
-
pluginList
method: Used to retrieve the list of all plugins from the plugin state storagePluginStoreState
. It creates a new plugin list by combining two arrays:pluginList
andcustomPluginList
. -
displayPluginList
method: Calls thepluginList
method to retrieve the merged plugin list and transforms thetitle
anddesc
into text displayed on the UI.
In components, the final consumed data can be directly obtained by importing:
import { usePluginStore } from '@/store/plugin';
import { pluginSelectors } from '@/store/plugin/selectors';
const Render = ({ plugins }) => {
const list = usePluginStore(pluginSelectors.displayPluginList);
return <> ... </>;
};
The benefits of implementing this approach are:
- Decoupling and reusability: By separating selectors from components, we can reuse these selectors across multiple components without rewriting data retrieval logic. This reduces duplicate code, improves development efficiency, and makes the codebase cleaner and easier to maintain.
- Performance optimization: Selectors can be used to compute derived data, avoiding redundant calculations in each component. When the state changes, only the selectors dependent on that part of the state will recalculate, reducing unnecessary rendering and computation.
- Ease of testing: Selectors are pure functions, relying only on the passed parameters. This means they can be tested in an isolated environment without the need to simulate the entire store or component tree.
- Type safety: As LobeChat uses TypeScript, each selector has explicit input and output type definitions. This provides developers with the advantage of auto-completion and compile-time checks, reducing runtime errors.
- Maintainability: Selectors centralize the logic for reading state, making it more intuitive to track state changes and management. If the state structure changes, only the relevant selectors need to be updated, rather than searching and replacing in multiple places throughout the codebase.
- Composability: Selectors can be composed with other selectors to create more complex selection logic. This pattern allows developers to build a hierarchy of selectors, making state selection more flexible and powerful.
- Simplified component logic: Components do not need to know the structure of the state or how to retrieve and compute the required data. Components only need to call selectors to obtain the data needed for rendering, simplifying and clarifying component logic.
With this design, LobeChat developers can focus more on building the user interface and business logic without worrying about the details of data retrieval and processing. This pattern also provides better adaptability and scalability for potential future changes in state structure.
This is the 🤯 / 🤖 Lobe Chat wiki. Wiki Home
- Architecture Design | 架构设计
- Code Style and Contribution Guidelines | 代码风格与贡献指南
- Complete Guide to LobeChat Feature Development | LobeChat 功能开发完全指南
- Conversation API Implementation Logic | 会话 API 实现逻辑
- Directory Structure | 目录架构
- Environment Setup Guide | 环境设置指南
- How to Develop a New Feature | 如何开发一个新功能:前端实现
- New Authentication Provider Guide | 新身份验证方式开发指南
- Resources and References | 资源与参考
- Technical Development Getting Started Guide | 技术开发上手指南
- Testing Guide | 测试指南