【发布时间】:2019-02-13 22:01:33
【问题描述】:
我正在尝试确定如何提取多条数据以在同一组件中使用。
我在 React/Redux 中看到的每个示例都请求非常具体的数据,并且有 reducer 和 action 来处理该确切类型的数据。但是,我无法找到有关处理更多通用数据的信息。
例如,我的网站上有几个不同的组件(或类别)。其中一个组件是Cards。因此,如果用户单击/cards/hockey 的链接,它应该从 API 请求曲棍球数据(如果它尚未在商店中),并将其显示在卡片页面中。如果用户单击/cards/football 的链接,它应该遵循相同的过程,检查它是否有存储的数据,如果没有从 API 中提取它,并显示带有该数据的卡片页面。
另一种组件类型可能是 stats,其中包含有关不同运动队的统计信息。
我不会总是提前知道有哪些类型的卡片可用,因此我无法在我的应用程序中硬编码特定的运动类型。
所以在这种情况下,我只想创建两个组件:卡片和统计信息,但要动态加载数据来填充这些组件。
现在我有太多的重复,而且是硬编码的。这意味着如果不创建新代码来处理这些类型中的每一个,我就无法在将来动态添加新类型。
例如,现在我有 /actions/footballCardActions.js 和 /actions/hockeyCardActions.js。然后我有 /reducers/footballCardReducers.js 和 /reducers/hockeyCardReducers.js。我可能也有类似的 Stats 组件。
我还要指定状态,例如 FETCH_HOCKEY_CARDS_SUCCESS 或 FETCH_FOOTBALL_CARDS_SUCCESS。
同样,这些都是硬编码的,这使得可扩展性变得困难。
我尝试遵循的一个示例是 https://scotch.io/tutorials/bookshop-with-react-redux-ii-async-requests-with-thunks - 但它再次使用非常具体的数据请求,而不是通用的。
我可以做些什么来使我的代码更通用,这样我就不需要对特定的数据集进行硬编码。有没有处理类似情况的好教程?
更多说明
我的一个组件(屏幕)是运动卡屏幕。菜单系统(带有链接)是在从 API 加载站点时自动生成的,因此我并不总是知道哪些链接可用。所以,可能会有曲棍球、足球以及其他一些我没有想到的运动的链接。单击菜单链接时,它将调用该运动类型的 API 并在运动卡屏幕上显示数据。
根据上面的链接(和其他类似网站),我已经弄清楚如何在动作和减速器部分对特定运动的每个请求进行硬编码,但我无法弄清楚如何做到这一点一般来说,如果我不提前了解这项运动。
基于当前答案的进一步说明
如果有人将一项名为 MuffiBall 的新运动添加到 API 数据库,我的应用程序需要能够处理它。因此,不能指望我为添加到 API 的每项新运动添加新的 JavaScript 代码。
从数据库中检索到的所有运动卡都遵循相同的结构。
我当前代码的概要
index.js
//index.js
//Other imports here (not shown)
import Cards from './components/CardsPage'
import * as cardActions from './actions/cardActions';
import * as statsActions from './actions/statsActions';
import configureStore from './store/configureStore';
const store = configureStore();
/* Bad place to put these, and currently I am expected to know what every sport is*/
store.dispatch(hockeyActions.fetchHockey());
store.dispatch(footballActions.fetchFootball());
store.dispatch(muffiballActions.fetchMuffiball());
render(
<Provider store={store}>
<Router>
<div>
/* Navigation menu here (not shown) */
/* Currently it is manually coded, */
/* but I will be automatically generating it based on API */
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/cards/:val" component={Cards} />
<Route path="/stats/:val" component={Stats} />
</div>
</Router>
</Provider>,
document.getElementById('app')
);
存储/configureStore.js
// store/configureStore.js
import {createStore, compose, applyMiddleware} from 'redux';
// Import thunk middleware
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
return createStore(rootReducer, initialState,
// Apply to store
applyMiddleware(thunk)
);
}
动作/动作类型
// actions/actionTypes
export const FETCH_HOCKEY_SUCCESS = 'FETCH_HOCKEY_SUCCESS';
export const FETCH_FOOTBALL_SUCCESS = 'FETCH_FOOTBALL_SUCCESS';
export const FETCH_MUFFIBALL_SUCCESS = 'FETCH_MUFFIBALL_SUCCESS';
actions/hockeyActions.js(每项运动都有一个这样的文件 - 需要制作这个通用文件):
// hockeyActions.js (one such file for every sport - need to make this one generic file):
import Axios from 'axios';
const apiUrl = '/api/hockey/';
// Sync Action
export const fetchHockeySuccess = (hockey) => {
return {
type: 'FETCH_HOCKEY_SUCCESS',
hockey
}
};
//Async Action
export const fetchHockey = () => {
// Returns a dispatcher function
// that dispatches an action at a later time
return (dispatch) => {
// Returns a promise
return Axios.get(apiUrl)
.then(response => {
// Dispatch another action
// to consume data
dispatch(fetchHockeySuccess(response.data))
})
.catch(error => {
console.log(error)
throw(error);
});
};
};
reducers/hockeyReducers.js(每项运动都有一个这样的文件 - 需要制作这个通用文件)
// reducers/hockeyReducers.js (one such file for every sport - need to make this one generic file)
import * as actionTypes from '../actions/actionTypes'
export const hockeyReducer = (state = [], action) => {
switch (action.type) {
case actionTypes.FETCH_HOCKEY_SUCCESS:
return action.hockey;
default:
return state;
}
};
reducers/index.js
// reducers/index.js
import { combineReducers } from 'redux';
import {hockeyReducer} from './hockeyReducers'
import {footballReducer} from './footballReducers'
import {muffiballReducer} from './muffiballReducers'
export default combineReducers({
hockey: hockeyReducer,
football: footballReducer,
muffiball: muffiballReducer,
// More reducers for each sport here
});
组件/CardsPage.js:
//components/CardsPage.js
import React from 'react';
import { connect } from 'react-redux';
class Cards extends React.Component{
constructor(props){
super(props);
this.state = {
data: this.props.data,
}
}
componentWillReceiveProps(nextProps){
this.setState({
data: nextProps.data,
})
}
render(){
return(
{/* cards displayed from this.state.data */}
)
}
}
const mapStateToProps = (state, ownProps) => {
return {
data: state[ownProps.match.params.val]
}
};
export default connect(mapStateToProps)(Cards);
【问题讨论】:
-
你需要克服什么问题? :) 正如您在问题的第一段中所问的那样,您在哪里以及如何获取数据?
-
@devserkan 基本上,我想弄清楚如何使用单个页面/组件/模板来显示不同的数据。在每种情况下,页面的结构都是相同的,但数据会根据用户从 API 中提取的内容而有所不同。所以,我需要一种方法来一般地提取数据并将其显示在页面上。例如,现在我需要执行“FETCH_HOCKEY_CARDS_SUCCESS”或“FETCH_FOOTBALL_CARDS_SUCCESS”之类的操作。我目前正在对这些进行硬编码。这会导致大量重复,并且不允许将来灵活添加其他卡。
-
我现在需要去,但是当我阅读您的问题时,我无法得出与您在此处解释的相同的结论。也许最好编辑你的问题。人们可以据此给出答案。不知何故,您想要可重用的动作创建者和缩减者?
-
@devserkan 是的,我相信这可能就是我想要的。
-
对于更通用的组件逻辑,请使用Higher order components。对于更通用的 reducer 逻辑,请使用 Higher order reducers。其他高阶 reducer 库可以在这里找到:github.com/markerikson/redux-ecosystem-links/blob/master/…
标签: javascript reactjs redux