React-Redux & Hooks: useSelector() and useDispatch()
Earlier in React, the
connect()
method (along with the associated
mapStateToProps() and
mapDispatchToProps() functions) is used to
connect a React component to the Redux store in the application.
However, with the advent of
Hooks, alternatives in the form of
useSelector() and
useDispatch() have become available.
In this tutorial, we will show, with an example, how to implement the
useSelector() and the
useDispatch() hooks in a React function
component.
We create directories /redux/reducers and
/redux/actions which will store two two
files inside each.
Inside redux/reducers, we save the first
reducer dog.js which stores the url of
some dog image.
// dog.js
const INITIAL_STATE = {
url: ''
}
export default (state = INITIAL_STATE, action={}) => {
switch(action.type) {
case "SET_DOG_DATA":
return {
...state,
...action.content
};
default:
return state;
}
};
The other reducer, quote.js, stores a
movie quote and the corresponding role. This is the second file stored
inside redux/reducers.
// quote.js
const INITIAL_STATE = {
quote: '',
role: ''
}
export default (state = INITIAL_STATE, action={}) => {
switch(action.type) {
case "SET_QUOTE_DATA":
return {
...state,
...action.content
};
default:
return state;
}
};
There is also an index.js file inside
redux/reducers where the two reducers are
combined into a single reducing function with the help of the
combineReducers()
function.
// index.js
import { combineReducers } from 'redux';
import dog from './reducers/dog';
import quote from './reducers/quote';
const singleReducer = combineReducers({
dog,
quote
});
export default singleReducer;
The combineReducers() function produces a
single state object, singleReducer, which
is later passed to the function
createStore inside the
store.js file. You may store this file in
whichever directory you deem fit, but for the purpose of the example
here it is stored in the root directory.
// store.js
import { createStore, applyMiddleware, compose } from 'redux';
import singleReducer from './redux/index';
import thunk from 'redux-thunk';
const store = createStore(
singleReducer,
compose (applyMiddleware(thunk),
window.devToolsExtension? window.devToolsExtension(): f => {return f})
);
export default store;
The other /redux/actions directory is
created for storing actions. Actions include type value. The first
action creator for dispatching dog image to the store is inside
dog.js.
// dog.js
import axios from 'axios';
const setDogData = (content) => {
return {
type: "SET_DOG_DATA",
content
}
}
const fetchDogImage = () => {
return (dispatch) => {
return axios.get('https://dog.ceo/api/breeds/image/random').then(response => {
if(response.status === 200) {
dispatch(
setDogData({
url: response.data.message
})
)
}
});
}
}
export {
setDogData,
fetchDogImage
}
And the other action creator for dispatching movie quote and the
corresponding role is inside quote.js.
// quote.js
import axios from 'axios';
const setQuoteData = (content) => {
return {
type: "SET_QUOTE_DATA",
content
}
}
const fetchQuote = () => {
return (dispatch) => {
return axios.get('https://movie-quote-api.herokuapp.com/v1/quote/').then(response => {
if(response.status === 200) {
dispatch(
setQuoteData({
quote: response.data.quote,
role: response.data.role
})
)
}
});
}
}
export {
setQuoteData,
fetchQuote
}
Below is a React function component which makes use of both the
useSelector() and the
useDispatch() hooks, without the need of
the
connect()
method. It is saved as
selectAndDispatch.js.
// selectAndDispatch.js
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchDogImage } from './redux/actions/dog';
import { fetchQuote } from './redux/actions/quote';
const App = () => {
const dogData = useSelector(state => state.dog);
const quoteData = useSelector(state => state.quote);
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchDogImage());
dispatch(fetchQuote());
console.log('quoteData', quoteData);
}, []);
return (
<div>
<img src={dogData.url} alt='dog'/>
<p>{quoteData.quote}</p>
<p>- {quoteData.role}</p>
</div>
);
}
export default App;
The function component
selectAndDispatch.js is invoked inside the
index.js file in the root directory and is
wrapped by the
<Provider/>
component to which is passed the Redux store as props.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import SelectAndDispatch from './selectAndDispatch';
ReactDOM.render(
<Provider store={store}>
<SelectAndDispatch/>
</Provider>
document.getElementById('root')
);
The component is rendered as follows:
We have made use of the useSelector() and
the useDispatch() hooks, without the use
of
connect()
and the associated mapStateToProps() and
mapDispatchToProps() functions.