Reputation: 21005
Completely new to React/Redux, and pretty confused about all the different ways to create components etc. I have read the async docs for Redux and managed to get async data loaded as the page loads, but now I want to use a button to trigger data download.
So far I have a components with this in the render
function.
<button onClick={() => dispatch(getEntry('dummy'))}> Get some data< /button>
This is reaching my action creator
export function getEntry(apiroute) {
return {
type: GET_ENTRY,
apiroute
};
}
and redux is passing this to my reducer
export function entry(state = initialState, action) {
switch (action.type) {
case 'GET_ENTRY':
return Object.assign({}, state, {
fetching : true
});
I can see in my logs that the state is being changed by the action.
In my actions file I have this code, which I know works if I wire it into to bootstrapping of my app.
export function fetchEntry() {
return function(dispatch) {
return window.fetch('/google.json')
.then(response => response.json())
.then(json => dispatch(recEntry(json)));
}
}
But where and how should I call fetchEntry
in the sequence above following the button click?
Upvotes: 2
Views: 4192
Reputation: 12780
The idiomatic way to do async is to use something like redux-thunk
or redux-promise
. redux-thunk
I think is more common though. You used the thunk pattern in your fetchEntry
function, but I don't think idiomatically. From the top:
Your button code looks good.
Your getEntry
action creator is a bit of a misnomer. You're using it more as initiateGetEntry
, right?
Using redux-thunk
, you'd combine the two action creators into a thunk action creator:
export function getEntry() {
return function(dispatch) {
dispatch({ type: 'GET_ENTRY_INITIATE' });
fetch('google.json)
.then(function(res) { dispatch({ type: 'GET_ENTRY_SUCCESS, payload: res }) }
.catch(function(res) { dispatch({ type: 'GET_ENTRY_FAIL' });
}
}
The reducer would be similar to what you had:
export function entry(state = initialState, action) {
switch (action.type) {
case 'GET_ENTRY_INITIATE':
return Object.assign({}, state, { fetching : true });
case 'GET_ENTRY_SUCCESS':
return Object.assign({}, state, action.payload, { fetching: false });
case 'GET_ENTRY_FAIL':
return Object.assign({}, state, { fetching: false, error: 'Some error' });
default:
return state;
}
Then, your UI code works fine:
<button onClick={() => dispatch(getEntry('dummy'))}> Get some data< /button>
Upvotes: 3
Reputation: 21005
OK, not sure whether this is the only way, but this worked in my Component
constructor(props) {
super(props);
this.getdata = this.getdata.bind(this);
}
getdata(evt) {
const { dispatch } = this.props;
dispatch(getEntry('getdata'));
fetchEntry()(dispatch);
}
render() {
return (
<div>
<button onClick={this.getdata}> Get some data2</button>
Basically I dispatch a message to the store that an update is starting, and then start the update.
Would welcome confirmation that this is an idiomatic pattern.
Upvotes: 0