每次我使用反应路由器导航到它时,如何阻止状态存储数据在 redux 组件中累积
How do I stop state store data from accumulating in a redux component every time I navigate to it using react router
好的,需要注意的是我对 redux 非常陌生。我正在 atm 上开设一门课程,我正在尝试跳出框框并使用 wordpress API 和 Redux 生成一个相当标准的网站。我很欣赏 redux 通常适用于更大的东西,但这似乎是学习过程中有用的第一步。
我有一系列组件,其中列出了 post、页面和取自 wordpress API 的不同类型的自定义 post,我使用 react- 在这些组件之间导航路由器-dom.
问题是,每次我返回 component/view 时,post 列表或页面都会再次呈现,例如,我第一次去那里时,列表可能是: test post 1, test post 2, 第二次是: test post 1, test post 2, test post 1, test post2、第三次:testpost1、testpost2、testpost1、testpost2、testpost1、测试 post 2 等等等等等等
这样做的原因很明显,每次呈现组件时,数据都会从商店中提取并呈现,但是,由于整个应用程序不会像使用普通的旧 reactjs 那样重新呈现,所以它不会' t 已清除。
我的问题当然是解决此问题的最佳方法是什么。我读过一些相关的 posts,建议将某种条件附加到组件以检查数据是否已经存在,但我不知道如何执行此操作,也无法找到如何执行此操作。我的尝试没有奏效,因为似乎在渲染方法中看不到从 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);
问题在于,每次获取数据时,都会将其添加到数组中的先前数据中。这就是为什么它会随着时间的推移而复制。只是 assign 而不是 adding 在你的 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;
}
}
希望对您有所帮助:)
据我了解,您只想在第一次安装时获取初始帖子,而不是每次安装组件时都获取?
在 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 个帖子或有一个 'Load more posts' 按钮。
您需要在调用 fetch 之前检查您的状态。我喜欢将我的大部分逻辑放在应用程序的 redux 部分(胖动作创建者),并仅使用我的反应组件来呈现当前状态。我会推荐这样的东西:
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 });
});
};
}
稍后,如果将帖子初始化为选择器并添加一些额外的层(例如帖子是否陈旧),您可以分离逻辑。这样你的 'business' 逻辑很容易测试并与你的 UI.
分开
希望对您有所帮助:)
好的,需要注意的是我对 redux 非常陌生。我正在 atm 上开设一门课程,我正在尝试跳出框框并使用 wordpress API 和 Redux 生成一个相当标准的网站。我很欣赏 redux 通常适用于更大的东西,但这似乎是学习过程中有用的第一步。
我有一系列组件,其中列出了 post、页面和取自 wordpress API 的不同类型的自定义 post,我使用 react- 在这些组件之间导航路由器-dom.
问题是,每次我返回 component/view 时,post 列表或页面都会再次呈现,例如,我第一次去那里时,列表可能是: test post 1, test post 2, 第二次是: test post 1, test post 2, test post 1, test post2、第三次:testpost1、testpost2、testpost1、testpost2、testpost1、测试 post 2 等等等等等等
这样做的原因很明显,每次呈现组件时,数据都会从商店中提取并呈现,但是,由于整个应用程序不会像使用普通的旧 reactjs 那样重新呈现,所以它不会' t 已清除。
我的问题当然是解决此问题的最佳方法是什么。我读过一些相关的 posts,建议将某种条件附加到组件以检查数据是否已经存在,但我不知道如何执行此操作,也无法找到如何执行此操作。我的尝试没有奏效,因为似乎在渲染方法中看不到从 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);
问题在于,每次获取数据时,都会将其添加到数组中的先前数据中。这就是为什么它会随着时间的推移而复制。只是 assign 而不是 adding 在你的 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;
}
}
希望对您有所帮助:)
据我了解,您只想在第一次安装时获取初始帖子,而不是每次安装组件时都获取?
在 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 个帖子或有一个 'Load more posts' 按钮。
您需要在调用 fetch 之前检查您的状态。我喜欢将我的大部分逻辑放在应用程序的 redux 部分(胖动作创建者),并仅使用我的反应组件来呈现当前状态。我会推荐这样的东西:
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 });
});
};
}
稍后,如果将帖子初始化为选择器并添加一些额外的层(例如帖子是否陈旧),您可以分离逻辑。这样你的 'business' 逻辑很容易测试并与你的 UI.
分开希望对您有所帮助:)