如何用Mobx实现本地Store?能否在组件级使用Store类而非顶层注入?
当然可行!这种组件级别的Store实例化方式在Mobx里其实非常常用,刚好匹配你说的「每个表格独立状态」的场景。我给你拆解一下具体怎么实现:
一、先创建可复用的TableStore类
首先定义一个Store类,把表格需要的状态(比如加载状态、数据、配置)和业务逻辑(比如Ajax请求)都封装进去。用Mobx的makeAutoObservable可以自动把属性和方法变成响应式的:
import { makeAutoObservable } from "mobx"; class TableStore { // 传入的初始化属性 tableId = ""; apiUrl = ""; // 表格状态 data = []; loading = false; error = null; constructor(initialProps) { // 把传入的属性赋值给Store Object.assign(this, initialProps); // 自动绑定响应式 makeAutoObservable(this); } // 封装Ajax请求方法 async fetchData() { this.loading = true; this.error = null; try { const response = await fetch(this.apiUrl, { // 可以根据tableId添加请求参数 headers: { "X-Table-Id": this.tableId } }); this.data = await response.json(); } catch (err) { this.error = err.message; } finally { this.loading = false; } } // 其他表格相关方法,比如排序、筛选 sortData(key) { this.data.sort((a, b) => a[key].localeCompare(b[key])); } } export default TableStore;
二、在组件级别实例化Store
接下来,在需要表格的页面组件里,直接创建Store实例就行。这里推荐用useRef来保存实例,因为它不会随组件重渲染而重新创建,保证每个组件实例只有一个Store:
import { useRef, useEffect } from "react"; import { observer } from "mobx-react-lite"; import TableStore from "./TableStore"; const TablePage = ({ tableId, apiUrl }) => { // 用useRef存Store实例,只初始化一次 const storeRef = useRef(); if (!storeRef.current) { storeRef.current = new TableStore({ tableId, apiUrl }); } const store = storeRef.current; // 组件挂载时触发初始数据请求 useEffect(() => { store.fetchData(); }, []); // 如果props变化(比如apiUrl更新),同步更新Store并重新请求 useEffect(() => { if (store.tableId !== tableId || store.apiUrl !== apiUrl) { store.tableId = tableId; store.apiUrl = apiUrl; store.fetchData(); } }, [tableId, apiUrl]); // 渲染表格 return ( <div className="table-container"> {store.loading && <p>加载中...</p>} {store.error && <p className="error">出错了:{store.error}</p>} {!store.loading && !store.error && ( <> <button onClick={() => store.sortData("name")}>按名称排序</button> <table> <thead> <tr> <th>ID</th> <th>名称</th> <th>数值</th> </tr> </thead> <tbody> {store.data.map(item => ( <tr key={item.id}> <td>{item.id}</td> <td>{item.name}</td> <td>{item.value}</td> </tr> ))} </tbody> </table> </> )} </div> ); }; // 一定要用observer包裹组件,这样才能响应Store的状态变化 export default observer(TablePage);
三、关键要点说明
- 为什么不用顶层Provider?:Provider适合全局共享的状态(比如用户信息、全局主题),而组件级别的Store不需要全局注入,这样每个表格组件的状态完全独立,不会互相干扰,也避免了全局状态的冗余和污染。
- 为什么用useRef?:如果用
useState来存Store,每次组件重渲染都会重新创建实例,而useRef的.current属性更新不会触发组件重渲染,能保证Store实例的唯一性和稳定性。 - 类组件怎么实现?:如果是类组件,可以在
constructor里实例化Store,然后用@observer装饰器包裹组件,示例如下:
import { Component } from "react"; import { observer } from "mobx-react"; import TableStore from "./TableStore"; @observer class TablePage extends Component { constructor(props) { super(props); this.store = new TableStore(props); } componentDidMount() { this.store.fetchData(); } componentDidUpdate(prevProps) { if (prevProps.tableId !== this.props.tableId || prevProps.apiUrl !== this.props.apiUrl) { this.store.tableId = this.props.tableId; this.store.apiUrl = this.props.apiUrl; this.store.fetchData(); } } render() { const { store } = this; // 渲染逻辑和函数组件类似 return <div>{/* ... */}</div>; } } export default TablePage;
这种方式完全满足你的需求:每个表格页面拥有独立的Store实例,能传入自定义属性,执行Ajax请求并管理表格状态,而且不需要在应用顶层注入任何东西。
内容的提问来源于stack exchange,提问作者DeltaTango
相关产品推荐
相关产品推荐

