I have a React component named ItemList
which loads a list of items from an API server and then renders them as a list of Item
components.
Each Item
has a delete button. When the button is clicked, I want to send a request to the API server to delete the item, then re-render the ItemList
.
One way I can think of to make that work is to move both API queries into reducers, and then dispatch actions whenever I want to a) get all items; and b) delete an item. Upon successful completion of those API operations, the reducer will update the store and the ItemList
will re-render.
Is that a reasonable approach, or is it a bad idea to put API calls inside of reducers?
Here's a simplified version of the the code I have so far. It doesn't yet use Redux. I want to make sure my approach is sound before implementing Redux, hence this Stack Overflow question.
ItemList.js
class ItemList extends Component {
constructor(props) {
super(props);
this.state = {
items: []
};
this.componentDidMount = this.componentDidMount.bind(this);
}
componentDidMount() {
const url = 'https://api.example.com/api/v1.0/item';
fetch(url, {
method: "get"
})
.then(res => res.json())
.then(response => {
this.setState({items: response.data});
});
}
render() {
<div>
{this.state.items.map((item, index) => (
<Item key={item.id} item={item} />
))}
</div>
}
}
Item.js
class Item extends Component {
deleteClicked() {
/**
* Is it ok to dispatch a "delete item" action
* from here and then make the actual API call
* in a reducer?
*/
}
render() {
<div>
<h2>{item.title}</h2>
<a onClick={this.deleteClicked}>delete item</a>
</div>
}
}
You're almost solved your task. To make you solution perfect use Action creators to make async calls and dispatch actions on completion. Reducer should be pure sync function.
For example ItemList
component may use such action creator to extract items
const ExtractItemsAction = () => (dispatch) => {
dispatch ({type: ITEMS_REQUESTED});
const url = 'https://api.example.com/api/v1.0/item';
fetch(url, {
method: "get"
})
.then(res => res.json())
.then(response => {
dispatch({type: ITEMS_RECEIVED, items: response.data});
});
}
And reducer will stay pure
function reducer (state = initalState, action)
{
switch (action.type) {
case ITEMS_REQUESTED:
return { ...state, itemsRequested: true }
case ITEMS_RECEIVED:
return { ...state, itemsRequested: false, items: action.items }
default
return state;
}
}
And don't forget to connect you component to Redux, and use Redux-thunk as middleware when creating store.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With