Redux-ORM数据更新时Selector未刷新问题求助
看起来你遇到的是Redux-ORM结合React时常见的状态追踪问题——本质是你的selector没有正确响应Redux store中ORM数据的变化,或者没有用合适的工具来确保状态更新时selector能重新计算并通知组件。下面一步步帮你解决:
1. 先确认Redux-ORM的Reducer集成正确
首先要保证你的Redux store里的ORM状态是通过官方的reducer包装器来管理的,否则API返回的数据根本没正确更新到ORM的状态中,selector自然不会触发更新。
比如,先定义你的schema和reducer:
// models/Author.js import { Model, attr } from 'redux-orm'; export class Author extends Model { static modelName = 'Author'; static fields = { name: attr(), age: attr(), }; } // schema.js import { Schema } from 'redux-orm'; import { Author } from './models/Author'; import { FETCH_AUTHORS_SUCCESS } from './actions/authors'; export const schema = new Schema({ Author, }); // 自定义处理action的reducer逻辑 export function ormReducer(state, action) { const session = schema.from(state); switch (action.type) { case FETCH_AUTHORS_SUCCESS: // 批量添加作者数据到ORM action.payload.forEach(author => { session.Author.create(author); }); break; // 其他action处理... } return session.state; } // reducers/index.js import { combineReducers } from 'redux'; import { ormReducer } from './schema'; export const rootReducer = combineReducers({ orm: ormReducer, // 其他reducer... });
2. 用Reselect创建带依赖追踪的ORM Selector
直接在mapStateToProps里创建session并查询的话,不仅性能差,还会导致Redux无法正确追踪状态变化——因为每次调用都会生成新的session对象,connect会误以为状态没变化。这时候reselect就派上用场了,它能缓存selector结果,只有当依赖的状态变化时才重新计算。
先安装reselect(如果还没装):
npm install reselect
然后创建你的selector:
// selectors/authors.js import { createSelector } from 'reselect'; import { schema } from '../schema'; // 第一步:获取store中的ORM状态 const getOrmState = state => state.orm; // 第二步:创建一个selector来获取ORM的session const getSession = createSelector( [getOrmState], ormState => schema.from(ormState) ); // 第三步:创建获取所有作者的selector export const getAuthors = createSelector( [getSession], session => session.Author.all().toModelArray() );
3. 确保API Action和Reducer正确处理数据
当API调用成功后,你的action要把返回的作者数据传递给reducer,reducer需要通过ORM的session来将数据添加到store中:
定义Action Types和Creators
// actions/authors.js export const FETCH_AUTHORS_SUCCESS = 'FETCH_AUTHORS_SUCCESS'; export const fetchAuthorsSuccess = authors => ({ type: FETCH_AUTHORS_SUCCESS, payload: authors, }); // 异步API调用action export const fetchAuthors = () => async dispatch => { try { const response = await fetch('/api/authors'); // 替换成你的API地址 const authors = await response.json(); dispatch(fetchAuthorsSuccess(authors)); } catch (error) { // 处理错误 console.error('Fetch authors failed:', error); } };
4. 在React组件中正确使用Selector
现在在你的组件里,用connect把selector和组件关联起来,注意不要在mapStateToProps里直接创建session,而是用我们之前定义好的getAuthors selector:
// components/AuthorsList.js import React, { Component } from 'react'; import { connect } from 'react-redux'; import { fetchAuthors } from '../actions/authors'; import { getAuthors } from '../selectors/authors'; class AuthorsList extends Component { componentDidMount() { // 在这里触发API调用(推荐用componentDidMount,componentWillMount已废弃) this.props.fetchAuthors(); } render() { const { authors } = this.props; if (!authors.length) { return <div>Loading authors...</div>; } return ( <ul> {authors.map(author => ( <li key={author.id}> {author.name}, {author.age} years old </li> ))} </ul> ); } } const mapStateToProps = state => ({ // 直接使用预定义的selector authors: getAuthors(state), }); const mapDispatchToProps = { fetchAuthors, }; export default connect(mapStateToProps, mapDispatchToProps)(AuthorsList);
关键原因总结
你之前的问题主要出在两个地方:
- 要么是ORM的状态没有正确更新(reducer集成错误或action处理不当)
- 要么是selector没有用reselect来做缓存和依赖追踪,导致Redux无法感知到ORM数据的变化,从而不会触发组件重新渲染
按照上面的步骤调整后,当API返回数据并更新store中的ORM状态时,reselect的selector会检测到依赖的ormState变化,重新计算作者列表,connect会把新的authors传递给组件,组件就会自动刷新了。
内容的提问来源于stack exchange,提问作者user581157

