Thunk Creators

You recall that the way to update the Redux store is by dispatching actions. For each action, you have an action creator that creates an object with keys type and payload. For example, this action creator builds an object for adding a name to the store:

function addName(name) {
return {type: Action.AddName, payload: name};
}

A component that stores a name in local state and wishes to commit it to the global store dispatches an action:

const [name, setName] = useState('');
const dispatch = useDispatch();

return (
{/* ... */}
<button onClick={() => dispatch(addName(name))}>Add</button>
);

The reducer then receives this action and updates the store.

When the state isn't local, you must first fetch it. Instead of writing an action creator, you write what might be called a thunk creator. A thunk is a block of unevaluated code that gets passed around a system. In your case, a thunk is an uncalled function. You might create this thunk creator to download the name from a web service:

function downloadName() {
return dispatch => {
fetch(url)
.then(/* ... */);
};
}

The function downloadName is a thunk creator because it returns a function, but does not execute it. The thunk it returns expects a dispatch function to be given as a parameter. Whoever calls your thunk will need to pass in an actual dispatch function.

Inside the thunk, you attach then-handlers to the promise that fetch yields to unpack the service's response:

function downloadName() {
return dispatch => {
fetch(url)
.then(assertResponse())
.then(response => response.json())
.then(data => {
dispatch(addName(data.name));
});
};
}

The final then-handler dispatches the action that updates the store using the passed dispatch function. All fetch-based thunk creators that you write for your Unforget client will have this general form. What will change is how you unpack the response data and what action creator you call.

To trigger the fetch, you dispatch the thunk just like you dispatch an action. For example, here the user clicks on a download button to fetch the name:

const dispatch = useDispatch();

return (
<button onClick={() => dispatch(downloadName())}>Download</button>
);

Internally, Redux will receive the thunk returned by downloadName. It will see that it isn't an action and will instead call the thunk, passing in its dispatch function.

Out of the box, however, plain Redux knows nothing about thunks. This code will fail if you've only installed react-redux. You must also install redux-thunk:

npm install redux-thunk

Then you must add thunk processing to the Redux store as middleware:

import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';

// ...

export const store = createStore(reducer, initialState, applyMiddleware(thunk));

This is how you pull down remote state when the user clicks a button. What if you wanted to pull down remote state when the app first loads?