我不知道是否每个人都读过这个:https://medium.com/@stowball/a-dummys-guide-to-redux-and-thunk-in-react-d8904a7005d3?source=linkShare-36723b3116b2-1502668727但它基本上教你如何用redux和redux thunk处理一个 API请求。这是一个很好的指南,但我想知道如果我的React组件只有一个获取请求到服务器?像这样:
componentDidMount() {
axios.get('http://localhost:3001/customers').then(res => {
this.setState({
res,
customer: res.data
})
})
axios.get('http://localhost:3001/events').then(res => {
this.setState({
res,
event: res.data
})
})
axios.get('http://localhost:3001/locks').then(res => {
this.setState({
res,
lock: res.data
})
})
}
我一直在谷歌搜索疯狂,我想我已经取得了一些进展,我的动作创建者目前看起来像这样(不知道它的100%是否正确):
const fetchData = () => async dispatch => {
try {
const customers = await axios.get(`${settings.hostname}/customers`)
.then(res) => res.json()
const events = await axios.get(`${settings.hostname}/events`)
.then(res) => res.json()
const locks = await axios.get(`${settings.hostname}/locks`)
.then(res) => res.json()
dispatch(setCustomers(customers))
dispatch(setEvents(events))
dispatch(setLocks(locks))
} catch(err) => console.log('Error', err)
}
所以下一步是创建你的减速器,我刚做了一个:
export function events(state = initialState, action) {
switch (action.type) {
case 'EVENTS_FETCH_DATA_SUCCESS':
return action.events;
default:
return state;
}
}
我现在不知道如何处理我的组件内部。如果您按照文章(https://medium.com/@stowball/a-dummys-guide-to-redux-and-thunk-in-react-d8904a7005d3?source=linkShare-36723b3116b2-1502668727)进行操作,最终会如下:
componentDidMount() {
this.props.fetchData('http://localhost:3001/locks')
}
和
Doors.propTypes = {
fetchData: PropTypes.func.isRequired,
doors: PropTypes.object.isRequired
}
const mapStateToProps = state => {
return {
doors: state.doors
}
}
const mapDispatchToProps = dispatch => {
return {
fetchData: url => dispatch(doorsFetchData(url))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Doors)
所以我的问题是我现在应该如何处理我的组件内的多个get请求?对不起,如果这个问题看起来很懒,但我真的无法理解,我真的一直在努力。
所有帮助都是超级赞赏!!
答案 0 :(得分:2)
假设您的请求不相互依赖,我建议将每个操作分开。该方法可能带来的好处:
动作:
const fetchCustomers = () => dispatch => {
try {
axios.get(`${settings.hostname}/customers`)
.then(res => {
dispatch(setCustomers(res.json()))
})
} catch(err) => console.log('Error', err)
}
const fetchEvents = () => dispatch => {
try {
axios.get(`${settings.hostname}/events`)
.then(res => {
dispatch(setEvents(res.json()))
})
} catch(err) => console.log('Error', err)
}
const fetchLocks = () => dispatch => {
try {
axios.get(`${settings.hostname}/locks`)
.then(res => {
dispatch(setLocks(res.json()))
})
} catch(err) => console.log('Error', err)
}
门组件摘录:
import * as actionCreators from './actions.js'
import { bindActionCreators } from 'redux'
/*...*/
componentDidMount() {
this.props.fetchCustomers();
this.props.fetchEvents();
this.props.fecthLocks();
}
const mapStateToProps = state => {
return {
customers: state.doors.customers,
events: state.doors.events,
locks: state.doors.locks
}
}
const mapDispatchToProps = dispatch => {
return bindActionCreators(actionCreators, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Doors)
编辑:添加了示例reducer并相应地采用了mapStateToProps
。在此reducer实例doors
中,此容器的状态为events
,locks
和customers
:
export function events(state = initialState, action) {
switch (action.type) {
case 'EVENTS_FETCH_SUCCESS':
return Object.assign({}, state, {events: action.events});
case 'CUSTOMERS_FETCH_SUCCESS':
return Object.assign({}, state, {customers: action.customers});
case 'LOCKS_FETCH_SUCCESS':
return Object.assign({}, state, {locks: action.locks});
default:
return state;
}
}
现在请求并行执行,它们独立失败。
答案 1 :(得分:1)
最佳做法是将状态(redux术语)分解为小块。
因此,不是拥有一个对象鸡尾酒[{event-object}, {customer-object}, {event-object},...so on]
的数组(状态结构),而是每个数据都有数组,然后是:每个数据的reducer。
export default combineReducers({
events,
customers,
locks,
})
如果我们同意这种架构,那么您的调度员,行动......将遵循这种架构。
然后:
export function getEvents() {
return (dispatch) => axios.get(`${settings.hostname}/events`)
.then(res) => res.json())
.then(events => dispatch(setEvents(events)))
}
// export function getCustomers ... so on
然后,在你的组件中,你需要顺序调度这三个调用,除非它们之间存在依赖关系:
import {getEvents, getCustomers, ... so on} from 'actions';
componentDidMount() {
this.props.dispatch(getEvents()) // you can use `connect` instead of `this.props.dispatch` through `mapDisptachToProps`
this.props.dispatch(getCustomers())
// .. so on
}
答案 2 :(得分:1)
您的events
缩减器应该更像:
export function events(state = initialState, action) {
switch (action.type) {
case 'EVENTS_FETCH_DATA_SUCCESS':
return Object.assign({}, state, {events: action.events});
default:
return state;
}
}
或者如果你有object spread transform,你可以这样做:
case 'EVENTS_FETCH_DATA_SUCCESS':
return {...state, events: action.events};
你应该总是使你的reducer成为纯函数,纯函数的一个要求是它不直接修改它的输入。您需要返回一个新的状态对象,并将更改合并到其中。
进行此更改后,您将需要一个类似的减速器用于其他操作(客户,锁)。
你的thunk中也有语法错误:
1) .then(res) => res.json()
应为:
.then(res => res.json())
2) catch(err) => console.log('Error', err)
应为:
catch(err) {
console.error('Error', err)
}
您已经在组件的正确轨道上了。我想你有Customers
和Locks
组件可以与你的Doors
组件一起使用。
但为什么要将API请求分组并按顺序执行?除非每个后续请求都需要先前响应中的数据,否则您可以并行执行这些操作并减少延迟。
然后,由于你的减速器正在写回复状态,你的mapStateToProps
,好......将状态映射到你的道具,你只需从你{{1}内的this.props.doors
读取}} 方法。如果要在等待道具填充实际值时显示加载器,可以添加一些条件逻辑,如:
render
现在,render() {
const { doors } = this.props;
return (
<div className="Doors">
{doors === null
? this.renderLoader()
: this.renderDoors()
}
</div>
);
}
renderLoader() {
return <Loader/>;
}
renderDoors() {
const { doors } = this.props;
return doors.map(door => (
<Door key={door.id}/>
));
}
呈现Doors
时,您可以调用原始componentDidMount
,当fetchData
道具发生变化时,您的组件会自动重新呈现。每个其他组件也是如此;当它们挂载时,它们会请求新数据,当数据到达时,您的thunk会调度存储更新,并且组件的props会响应该更新而更改。
假设您的API流程实际上非常复杂,它要求每个API请求都知道先前请求中的某些数据,例如,如果您要获取客户最喜欢的门牌,但doors
端点仅返回您fetchCustomer
,因此您必须致电favoriteDoorID
。
现在你有一个真正的用例,你的组合thunk看起来像:
fetchDoor(favoriteDoorID)
然后你会在你的一个reducers中创建一个case,监听动作类型const fetchFavoriteDoorForCustomer = (customerID) => async dispatch => {
try {
const customer = await axios.get(`${settings.hostname}/customer/${customerID}`)
.then(res => res.json());
const doorID = customer.favoriteDoorID;
const favoriteDoor = await axios.get(`${settings.hostname}/door/${doorID}`)
.then(res => res.json());
dispatch(receivedFavoriteDoor(favoriteDoor));
} catch(err) {
console.error('Error', err);
}
}
或其他类似的东西,这会将它合并到我在第一部分写的状态。
最后,需要此数据的组件将使用RECEIVED_FAVORITE_DOOR
订阅商店,并使用相应的connect
函数:
mapStateToProps
所以现在你可以从const mapStateToProps = state => {
return {
favoriteDoor: state.favoriteDoor
}
}
阅读,你很高兴。
希望有所帮助。