每次使用反应路由器导航到redux组件时,如何阻止状态存储数据在redux组件中累积

Posted

技术标签:

【中文标题】每次使用反应路由器导航到redux组件时,如何阻止状态存储数据在redux组件中累积【英文标题】:How do I stop state store data from accumulating in a redux component every time I navigate to it using react router 【发布时间】:2020-05-11 11:23:43 【问题描述】:

好的,需要注意的是我对 redux 非常陌生。我正在 atm 上做一门课程,我试图跳出框框,使用 wordpress API 和 Redux 生成一个相当标准的网站。我很欣赏 redux 通常适用于更大的事情,但这似乎是学习过程中有用的第一步。

我有一系列组件,其中列出了从 wordpress API 获取的帖子、页面和不同类型的自定义帖子,我使用 react-router-dom 在这些之间导航。

问题是每次我回到一个组件/视图时,帖子或页面的列表都会再次呈现,例如,我第一次去那里时,列表可能是:测试帖子 1,测试帖子 2,第二次是:测试站 1,测试站 2,测试站 1,测试站 2,第三次:测试站 1,测试站 2,测试站 1,测试站 2,测试站 1,测试站 2等等等等等等。

原因很明显,每次渲染组件时,数据都会从 store 中提取并渲染,但是,由于整个应用程序不会像使用普通的旧 reactjs 那样重新渲染,因此不会清除.

我的问题当然是解决此问题的最佳方法是什么。我已经阅读了一些相关的帖子,这些帖子建议在组件上附加某种条件以检查数据是否已经存在,但我不知道如何做到这一点,也不知道如何做到这一点。我的尝试没有奏效,因为在 render 方法中似乎没有看到从 componentDidMount 返回的任何 var。

提前致谢。

代码如下:

src/index.js

import React from "react";
import  BrowserRouter as Router  from 'react-router-dom';
import  render  from "react-dom";
import  Provider  from "react-redux";
import store from "./js/store/index";
import App from "./js/components/App";
render(
  <Router>
    <Provider store=store>
      <App />
    </Provider>
  </Router>,
  document.getElementById("root")
);

src/js/index.js

import store from "../js/store/index";
window.store = store;

src/js/store/index.js

import  createStore, applyMiddleware, compose  from "redux";
import rootReducer from "../reducers/index";
import thunk from "redux-thunk";
const storeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
  rootReducer,
  storeEnhancers(applyMiddleware(thunk))
);
export default store;

src/js/reducers/index.js

import  POSTS_LOADED  from "../constants/action-types";
import  PAGES_LOADED  from "../constants/action-types";

const initialState = 
  posts: [],
  pages: [],
  banner_slides: [],
  portfolio_items: []
;

function rootReducer(state = initialState, action) 

    switch (action.type) 
      case 'POSTS_LOADED':
          return Object.assign(, state, 
            posts: state.posts.concat(action.payload)
          );

      case 'PAGES_LOADED':
          return Object.assign(, state, 
            pages: state.pages.concat(action.payload)
          );

      default: 
          return state;
    


export default rootReducer;

src/js/actions/index.js

export function getWordpress(endpoint) 
    return function(dispatch) 
        return fetch("http://localhost/all_projects/react-wpapi/my_portfolio_site/wordpress/wp-json/wp/v2/" + endpoint )
            .then(response => response.json())
            .then(json =>  
            dispatch( type: endpoint.toUpperCase() + "_LOADED", payload: json );
        );
    ;

src/js/constants/action-types.js

export const ADD_ARTICLE = "ADD_ARTICLE";
export const POSTS_LOADED = "POSTS_LOADED";
export const PAGES_LOADED = "PAGES_LOADED";

src/js/components/app.js

import React from "react";
import  Route, Switch, Redirect  from 'react-router-dom';

import Header from "./Header/Header";
import Posts from "./Posts";
import Pages from "./Pages";
import BannerSlides from "./BannerSlides";
import PortfolioItems from "./PortfolioItems";

const App = () => (
    <div>
        <Header />
        <Route render = ( location ) => (
            <Switch location=location>
            <Route 
                exact path="/posts"
                component=Posts 
            />   
            <Route 
                exact path="/pages"
                component=Pages 
            />   
            </Switch>
        ) />
    </div>
);
export default App;

src/js/components/Posts.js

import React,  Component  from "react";
import  connect  from "react-redux";
import  getWordpress  from "../actions/index";
export class Posts extends Component 

  componentDidMount() 
      this.props.getWordpress('posts');
      let test = 1;
      return test;
  

  render() 
    console.log("test: ", test); // not defined
      if (test !== 1)  
          return (
            <ul>
              this.props.posts.map(item => ( 
                <li key=item.id>item.title.rendered</li>
              ))
            </ul>
          );
      
  

function mapStateToProps(state) 
    return 
        posts: state.posts.slice(0, 10)
    ;

export default connect(
  mapStateToProps,
   getWordpress 
)(Posts);

【问题讨论】:

【参考方案1】:

问题在于,每次获取数据时,都会将其添加到数组中的先前数据中。这就是为什么它会随着时间的推移而重复。只需 assign 而不是 add 在你的 reducer 中

function rootReducer(state = initialState, action) 
switch (action.type) 
  case 'POSTS_LOADED':
      return 
        ...state,
        posts: action.payload
      ;

  case 'PAGES_LOADED':
      return 
        ...state,
        pages: action.payload
      ;

  default: 
      return state;
 

希望对你有帮助:)

【讨论】:

太棒了,谢谢。现在看它,我意识到这是“concat”在做的,但这种方法最简洁并且效果很好。出于兴趣,原来的 reducer 正在做一个赋值(尽管使用了更长的语法)。你给我看的方法怎么知道是要赋值的。它只是隐含在代码/默认位置中吗?谢谢:-) 它被称为“扩展语法”,它的工作方式几乎与Object.assign 类似,但有一些不同的行为。大概 95% 的时间你可以使用扩展语法,它更短,更易读。您可以查看有关它的文档:developer.mozilla.org/en-US/docs/Web/javascript/Reference/…,以及这些方法之间的区别:***.com/questions/32925460/… 干杯哥们,感谢 ;-)【参考方案2】:

如果我理解,您只想在第一次安装时获取初始帖子,而不是每次安装组件时?

src/js/components/Posts.js 中,您可以在 CDM 生命周期方法中获取之前检查是否有任何帖子存储在 Redux 中。例如。

componentDidMount() 
  // access props.posts which you set inside mapDispatchToProps
  if (this.props.posts.length === 0) 
    this.props.getWordpress('posts');
  

如果您可以在每次挂载时重复 API 调用,并且可以一次获取所有帖子,您可以调整减速器以覆盖帖子数组而不是 concat。但覆盖它假设您想在 1 个 API 调用中加载所有帖子,而不是每页加载 25 个帖子或使用“加载更多帖子”按钮。

【讨论】:

【参考方案3】:

您需要在调用 fetch 之前检查您的状态。我喜欢将我的大部分逻辑放在应用程序的 redux 部分(胖动作创建者),并且只使用我的 react 组件来呈现当前状态。我会推荐这样的东西:

    export function getWordpress(endpoint) 
        return function(dispatch, getState) 
            const currentState = getState();
            if (currentState.posts && currentState.posts.length) 
            // already initialized, can just return current state
                return currentState.posts;
            
            return fetch("http://localhost/all_projects/react-wpapi/my_portfolio_site/wordpress/wp-json/wp/v2/" + endpoint )
                .then(response => response.json())
                .then(json =>  
                dispatch( type: endpoint.toUpperCase() + "_LOADED", payload: json );
            );
        ;
    

稍后,如果帖子被初始化到选择器中,您可以分离逻辑并添加一些额外的层(例如帖子是否陈旧)。这样,您的“业务”逻辑就可以轻松测试并与您的 UI 分离。

希望这会有所帮助:)

【讨论】:

以上是关于每次使用反应路由器导航到redux组件时,如何阻止状态存储数据在redux组件中累积的主要内容,如果未能解决你的问题,请参考以下文章

反应如何在 redux-saga 中导航路由器?

React s-s-r路由器-导航到子路由时的redux状态丢失

反应导航以从 redux 操作文件导航

当我禁用redux-devtools时,路由器导航需要很长时间。

Redux 使用 react-router-redux 连接“块”导航

Redux&React路由器:梳理调度和导航(history.push)