在路由加载时将模型加载到 redux 存储的最佳方法

Posted

技术标签:

【中文标题】在路由加载时将模型加载到 redux 存储的最佳方法【英文标题】:Best Way to load model into redux store on route load 【发布时间】:2018-12-19 03:43:56 【问题描述】:

我有一个 React 应用程序,它使用 React-Router/React-Router-dom 进行页面导航和 redux 来存储一些全局状态信息(例如 django rest 框架的 jwt 令牌)。该状态还存储有关当前查看的页面的信息,例如序列化的 django 模型。

但是当路由发生变化时,将 django 模型加载到 redux 存储中的最佳方法是什么?我在思考逻辑应该走向何方时遇到了麻烦。

如果您查看下面的 repo,您​​会发现我在哪里遇到了麻烦。

在此示例中,当有人导航到 /spells/:id 时,它应该将 spell django 模型加载到 redux 存储中,以便全局可访问有关它的信息。

但是我该怎么做呢?我在哪里调用 action 和 reducer 来正确处理状态?

任何指导将不胜感激。

您可以查看完整的项目here。这里有问题的组件是LayoutSpellView (/frontend/src/components/LayoutSpellView)。这就是模型信息存储、显示等的地方。

编辑:添加相关代码

致电componentDidMount:

axios
    .get("http://localhost:3000/api/spells/" + spellId)
    .then(response => 
        let spell = Object.assign(, spellView.state.spell);

        spell.id =  response.data.id;
        spell.owner =  response.data.owner;
        ...blahblah other fields

        this.setState(
            spell
        );


    )
    .then(response => 
        this.props.dispatch(
            type: 'FETCH_SPELL_SUCCESS',
            payload: this.state.spell,
        );
    )
    .catch(function(error) 
        console.error('[API]\t', error);
    );

LayoutSpellView(与上面相同的组件)

import loadSpell from "../src/reducers";
const mapStateToProps = (state) => (
    spell: loadSpell(state.spell.id),
);
const mapDispatchToProps = (dispatch) => (
    getSpell: (state.spell.id) => 
        dispatch(loadSpell(state.spell.id))
    
);

动作 spell.js:

export const FETCH_SPELL = '@@spell/FETCH_SPELL';
export const FETCH_SPELL_SUCCESS = '@@spell/FETCH_SPELL_SUCCESS';
export const FETCH_SPELL_FAILURE = '@@spell/FETCH_SPELL_FAILURE';

export const loadSpell = (spellId) => (
    [RSAA]: 
        endpoint: '/api/spell/$spellId',
        method: 'GET',
        types: [
            FETCH_SPELL, FETCH_SPELL_SUCCESS, FETCH_SPELL_FAILURE
        ]
    
);

减速器 spell.js:

const initialState = 
    spell: 
        id: 0,
        owner: 0,
        Name: 'Name',
        School: 'unknown',
        Subschool: 'unknown',
    
;
export default (state=initialState, action) => 
    switch(action.type) 
        case spell_action.FETCH_SPELL_SUCCESS:
            return 
                spell: 
                    id: action.payload.spell.id,
                    owner: action.payload.spell.owner,
                    Name: action.payload.spell.Name,
                    School: action.payload.spell.School,
                    Subschool: action.payload.spell.Subschool,
                
            ;
        default:
            return state;
    


export function loadSpell(state) 
    if (state) 
        return state.spell
    

【问题讨论】:

我可能会在组件的componentDidMount 中调用一个操作来检索/存储数据。 @NorianNyx 我想了这么多。这实际上是我有一个 axios 调用来检索它的地方。但是在哪里以及如何调用操作以从本地以外的地方推送新状态? 您可以在您的 axios 调用的 .then 方法中调用它,例如:this.props.dispatch( type: 'RETRIEVE_SUCCESS', payload: res.data )(或任何您的调度操作模式)。然后在你的减速器中:case 'RETRIEVE_SUCCESS': (state, action) => ( ...state, action.payload ) @NorianNyx 我想我做到了,但它不断给我错误,它是未定义的。我在我的问题中添加了相关的代码。我做得对吗? 负载是否未定义? 【参考方案1】:

让我们换个角度来看这个问题。与其问“当路由改变时我如何调度一个动作”,不如问“什么是真实的真实来源:Redux 还是 URL?”

如果我们选择 redux 作为单一真相来源,那么这意味着我们需要 dispatch 一些 action 会导致一些副作用(可能是 redux-sagaredux-observable 甚至redux-thunk?) 改变了 url:

Comp -> dispatch(action) -> store updates -> URL changes

如果我们将 URL 作为唯一的真相来源,我们将流程更改为:

URL changes -> dispatch(action) -> store updates

如果我们走这条路,这听起来像你想要的,你可能需要连接middleware,这是以下签名的函数:

store => next => action => next(action)

根据您使用的路由器,您可以挂钩他们的操作,也可以挂钩window.onpopstate 并检查下一个网址。无论哪种方式,整个中间件功能看起来都像

const middleware = store => 
  return next => action => 
    if (actionWillCauseSpellToBeNeeded(action)) 
       makeAPICall()
         .then(transformAPIToAction)
         .catch(transformError)
         .then(store.dispatch) 
    
    return next(action)
  

【讨论】:

以上是关于在路由加载时将模型加载到 redux 存储的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

如何将 json 文件加载到本地存储中?

将自定义对象存储到 Android 内部存储的最佳方式?

如何在更新 Laravel 中的记录时将表单发布数据加载到模型中

在页面加载时将服务器端 HTML 转换为 Javascript MVC 的最佳方法是啥?

java-项目加载时将数据存储到内存中

Redux 商店使用 nextJS 自行重置路由更改