I am trying to integrate redux to an existing react application. I am learning redux. I am using hooks. In the internet there are many examples of using class components. I could not find an example how to achieve this with a function component.
The code below does not throw an error. But, the action inside useEffect
is not called. The code does not produce any output. I wonder if the call is correct or not. Any help?
Index.js
const store = createStore(rubricReducer, applyMiddleware(thunk));
ReactDOM.render(
<BrowserRouter basename={baseUrl}>
<Provider store={store} > <App /> </Provider>
</BrowserRouter>,
rootElement);
Rubrics.tsx
const mapStateToProps = state => ({
rubrics: state.rubrics.items,
loading: state.rubrics.loading,
error: state.rubrics.error
});
const mapDispatchToProps = (dispatch) => {
return {
getRubrics: () => dispatch(fetchRubrics())
};
}
const Rubrics = (props) => {
const { rubrics, loading, error } = props;
useEffect(() => {
props.dispatch(fetchRubrics());
}, []);
if (error) { return <div>Error! {error.message}</div>; }
if (loading) { return <div>Loading...</div>; }
return (
<ul>
<React.Fragment>
{rubrics.map(rubric => {
return (
<li key={rubric.id}>{rubric.title}</li>
);
})}
</React.Fragment>
</ul>
)
}
export default connect(mapStateToProps, mapDispatchToProps)(Rubrics);
rubricActions.tsx:
export function fetchRubrics() {
return dispatch => {
dispatch(fetchRubricsBegin());
axios.get("api/Rubric/Index")
.then(response => {
console.log('success: reading rubrics');
dispatch(fetchRubricsSuccess(response.data));
return response.data;
})
.catch(error => { fetchRubricsFailure(error) });
};
}
export const FETCH_RUBRICS_BEGIN = "FETCH_RUBRICS_BEGIN";
export const FETCH_RUBRICS_SUCCESS = "FETCH_RUBRICS_SUCCESS";
export const FETCH_RUBRICS_FAILURE = "FETCH_RUBRICS_FAILURE";
const fetchRubricsBegin = () => ({
type: FETCH_RUBRICS_BEGIN
})
const fetchRubricsSuccess= (rubrics) => ({
type: FETCH_RUBRICS_SUCCESS,
payload: { rubrics}
})
const fetchRubricsFailure = (error) => ({
type: FETCH_RUBRICS_FAILURE,
payload: {error}
})
rubricReducer.tsx :
import {
FETCH_RUBRICS_BEGIN,
FETCH_RUBRICS_SUCCESS,
FETCH_RUBRICS_FAILURE
} from '../_actions/rubricActions';
const initialState = {
rubrics: [],
loading: false,
error: null
};
const rubricReducer = (state = initialState, action) => {
switch (action.type) {
case FETCH_RUBRICS_BEGIN:
return {
...state,
loading: true,
error: null
};
case FETCH_RUBRICS_SUCCESS:
return {
...state,
loading: false,
items: action.payload.rubrics
}
case FETCH_RUBRICS_FAILURE:
return {
...state,
loading: false,
error: action.payload.error,
items: []
};
default:
return state;
}
}
export default rubricReducer;
Note: React guarantees that dispatch function identity is stable and won't change on re-renders. This is why it's safe to omit from the useEffect or useCallback dependency list. You would need to pass either bound action creators or a reference to dispatch to your hook.
Dispatching an action within a reducer is an anti-pattern. Your reducer should be without side effects, simply digesting the action payload and returning a new state object. Adding listeners and dispatching actions within the reducer can lead to chained actions and other side effects.
The return function is the cleanup function, or when the user leaves the page and the component will unmount. The array is the last part, and it is where you put the states that will update throughout the component's lifecycle. You can discard the array if your component won't update during its lifecycle.
With the hooks provided by react-redux since v7.1.0, this can be now written without mapStateToProps
and mapDispatchToProps
.
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
const Rubrics = () => {
const dispatch = useDispatch();
const { rubrics, loading, error } = useSelector(
state => ({
error: state.error,
rubrics: state.rubrics,
loading: state.loading
})
);
useEffect(() => {
dispatch(fetchRubrics());
}, [dispatch]);
if (error) { return <div>Error! {error.message}</div>; }
if (loading) { return <div>Loading...</div>; }
return (
<ul>
<React.Fragment>
{rubrics.map(rubric => {
return (
<li key={rubric.id}>{rubric.title}</li>
);
})}
</React.Fragment>
</ul>
)
};
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