React Redux state array changes are not re-rendering a component - javascript

I have a project that uses React + Redux + Thunk and I am rather new to the stack. I have a scenario where I am fetching an array from an API call in my action/reducer, but it is not re-rendering in a component/container that is hooked up to the Store. The component does render the first time when I fire up the app, but at that point the array is undefined when logged to console.
I am trying to display the array's length, so this is always resulting in 0. With ReduxDevTools I see that the state of network_identities does populate correctly and is longer zero... Where am I going wrong?
Here is my sample action
///////////// Sample action /////////////
import axios from 'axios';
const url = 'sample#url.com';
const authorization = 'sample_auth';
export function fetchConnections() {
const params = {
headers: {
authorization,
},
};
return (dispatch) => {
// call returns an array of items
axios.get(`${url}/connection`, params)
.then((connections) => {
let shake_profiles = [];
let connected_profiles = [];
let entity_res;
// map through items to fetch the items data, and split into seperate arrays depending on 'status'
connections.data.forEach((value) => {
switch (value.status) {
case 'APPROVED': case 'UNAPPROVED':
{
axios.get(`${url}/entity/${value.entity_id_other}`, params)
.then((entity_data) => {
entity_res = entity_data.data;
// add status
entity_res.status = value.status;
// append to connected_profiles
connected_profiles.push(entity_res);
});
break;
}
case 'CONNECTED':
{
axios.get(`${url}/entity/${value.entity_id_other}`, params)
.then((entity_data) => {
entity_res = entity_data.data;
entity_res.status = value.status;
shake_profiles.push(entity_res);
})
.catch(err => console.log('err fetching entity info: ', err));
break;
}
// if neither case do nothing
default: break;
}
});
dispatch({
type: 'FETCH_CONNECTIONS',
payload: { shake_profiles, connected_profiles },
});
});
};
}
Sample Reducer
///////////// Sample reducer /////////////
const initialState = {
fetched: false,
error: null,
connections: [],
sortType: 'first_name',
filterType: 'ALL',
shake_identities: [],
network_identities: [],
};
const connectionsReducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_CONNECTIONS':
console.log('[connections REDUCER] shake_profiles: ', action.payload.shake_profiles);
console.log('[connections REDUCER] connected_profiles: ', action.payload.connected_profiles);
return { ...state,
fetched: true,
shake_identities: action.payload.shake_profiles,
network_identities: action.payload.connected_profiles,
};
default:
return state;
}
};
export default connectionsReducer;
Sample Store
///////////// Sample Store /////////////
import { applyMiddleware, createStore, compose } from 'redux';
import thunk from 'redux-thunk';
import promise from 'redux-promise-middleware';
import reducers from './reducers';
const middleware = applyMiddleware(promise(), thunk);
// Redux Dev Tools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducers, composeEnhancers(middleware));
export default store;
Sample Component - see if the API is done fetching the array, then display the length of the array
///////////// Sample Component /////////////
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
import { bindActionCreators } from 'redux';
import CSSModules from 'react-css-modules';
import * as ConnectionActions from 'actions/connections';
import styles from './styles.scss';
function mapStateToProps(state) {
return {
network_identities: state.connections.network_identities,
loadedConnections: state.connections.fetched,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Object.assign({}, ConnectionActions), dispatch),
};
}
class Counter extends Component {
componentWillMount() {
const { network_identities, actions } = this.props;
if (!network_identities.length) {
console.log('||| fetching Connections');
actions.fetchConnections();
}
}
render() {
let { network_identities, loadedConnections} = this.props;
console.log('[Counter] network_identities[0]: ', network_identities[0]);
console.log('[Counter] network_identities: ', network_identities);
console.log('[Counter] loadingConnections: ', loadingConnections);
return (
<div>
<Link to="/network">
<div>
<span>Connections</span>
{ !loadedConnections ? (
<span><i className="fa fa-refresh fa-spin" /></span>
) : (
<span>{network_identities.length}</span>
) }
</div>
</Link>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CSSModules(Counter, styles));
I suspect I am either mutating the state in my reducer, or I am misusing Thunk.

The problem in the code is that connections.data.forEach((value) => {..}) will send out a bunch of fetches, and then immediately return without waiting for the result arrays to be populated. A 'FETCH_CONNECTIONS' action is dispatched with empty arrays, and all connected components will rerender with the empty results.
What makes it tricky though is that the array objects that you put in the store will get pushed to once the fetches finish, so when you inspect the store it will seem populated correctly.
Not using any mutations will prevent the accidental population of the store, but won't solve the fact that dispatch is fired before the results are in. To do that, you could either create actions to add single results and dispatch those in the axios.get().then parts, or you could create a list of promises and wait for all of them to resolve with Promise.all().
Here's what the latter solution could look like.
axios.get(`${url}/connection`, params)
.then((connections) => {
const connectionPromises = connections.data.map((value) => {
switch (value.status) {
case 'APPROVED': case 'UNAPPROVED':
return axios.get(`${url}/entity/${value.entity_id_other}`, params)
.then((entity_data) => {
return {connected_profile: {...entity_data.data, status: value.status}};
});
case 'CONNECTED':
return axios.get(`${url}/entity/${value.entity_id_other}`, params)
.then((entity_data) => {
return {shake_profile: {...entity_data.data, status: value.status}};
})
// if neither case do nothing
default:
return {};
}
});
Promise.all(connectionPromises)
.then((connections) => {
const connected_profiles =
connections.filter((c) => c.connected_profile).map((r) => r.connected_profile);
const shake_profiles =
connections.filter((c) => c.shake_profile).map((r) => r.shake_profile);
dispatch({
type: 'FETCH_CONNECTIONS',
payload: { shake_profiles, connected_profiles },
});
}).catch(err => console.log('err fetching entity info: ', err));
});
You'll probably want to use some more appropriate names though, and if you use lodash, you can make it a bit prettier.

The issue here is that you are making an async operation within a componentWillMount. When this lifecycle method is called,it does not block the render method from being called. That is, it does not wait until there is a response from its operations. So, rather move this async action to componentDidMount.

Related

Unable to pass state from reducer to 'this.props', fetching API using axios on React-Redux

Hi I'm very new to React and this is my first project, what I'm trying to do is when onClick, the code will fetch data from API using Axios and display the object.
First, I tried using a reducer and failed. Read on Redux docs, that the way to resolve when dealing with asynchronous actions we to use applymiddleware. So after trying and adjusting I end up with:
// I KNOW... YOU MIGHT BE THINKING WHY REDUX FOR SUCH A SIMPLE APP, WHY NOT JUST USE REACT?? IT'S SUCH AN OVER-KILL. WELL... I FORCE MYSELF TO IMPLEMENT IT ON THE FIRST PROJECT TO UNDERSTAND AND DEMONSTRATE THE FUNCTIONALITY OF REACT, REDUX AND REACT-REDUX. SIMPLE APP IS IT CLEARER/OBVIOUS.
const thunk = ReduxThunk.default;
const { Provider, connect } = ReactRedux;
const { createStore, applyMiddleware } = Redux;
const GENERATE = 'GENERATE';
class QuoteGenerator extends React.Component {
generateQuote = () => {
this.props.dispatch(generateNewQuote());
};
generate = () => {
this.props.dispatch({
type: GENERATE,
});
};
render() {
console.log(this.props);
return (
<div>
<h1>{this.props.quote}</h1>
<button onClick={this.generate}>Generate</button>
</div>
);
}
}
const mapStateToProps = (state) => ({
quote: state.quote,
});
const Container = connect(mapStateToProps)(QuoteGenerator);
/**************************************************************/
const initState = {
quote: 'Generate Me!',
};
function fetchQuote() {
return axios.get(
'https://gist.githubusercontent.com/camperbot/5a022b72e96c4c9585c32bf6a75f62d9/raw/e3c6895ce42069f0ee7e991229064f167fe8ccdc/quotes.json'
);
}
function returnQuote(quote) {
return {
quote: quote,
};
}
// But what do you do when you need to start an asynchronous action,
// such as an API call, or a router transition?
// Meet thunks.
// A thunk is a function that returns a function.
// This is a thunk.
function generateNewQuote(state = initState, action) {
// if (action.type === GENERATE) {
// console.log("It Works")
// Invert control!
// Return a function that accepts `dispatch` so we can dispatch later.
// Thunk middleware knows how to turn thunk async actions into actions.
return function (dispatch) {
return fetchQuote().then((res) => {
const selectRandomQuote = Math.floor(
Math.random() * res.data.quotes.length
);
const quoteObj = {
quote: res.data.quotes[selectRandomQuote]['quote'],
author: res.data.quotes[selectRandomQuote]['author'],
};
console.log({ quote: res.data.quotes[selectRandomQuote]['quote'] });
return { quote: res.data.quotes[selectRandomQuote]['quote'] };
});
};
// } else {
// return state;
// }
}
function reducer(state = initState, action) {
if (action.type === GENERATE) {
return {
quote: 'It Works!',
};
} else {
return state;
}
}
// applyMiddleware supercharges createStore with middleware:
const store = createStore(generateNewQuote, applyMiddleware(thunk));
class App extends React.Component {
render() {
return (
<Provider store={store}>
<Container />
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
I'm able to trigger the function and console log the output but with no luck, it's not showing...
I really appreciate the guidance to share some light on this, been really keen on React but the first has hit me hard. Thanks in advance, cheers!

React need to click twice to render data fetched from api

I need to click twice on a button linked to the current page so i can get the data fetched from the api to render. I am using nivo / charts to visualize my data.
The component fetches the company list from the api, and a second fetch loops through every result fetching data for every distinct company.
On first try, the company list were to fetch on the parent component, and a fetch request would take place for every child component thereafter
(parent=list of chart components, child=Company Chart), but on the pagination process it did not render properly so I had to uplifted the state to the parent component, the problem this time was that the parent component did not render on first click, I had to double click for example link button so that the parent component would render.
I thought the problem might be occurring since there might had been a missynchronization with the componentDidMount order of actions since I was sure that the first and second data fetching (first being the company get request and second distinct company get request), were executing simultaneously rather than one after the other. So I directed to redux and architectured my application to redux rules. It did not resolve anything and still requires to double click on a link so that the rendering would take place.
Now I feel like I would need to add some await/async rules for the api fetching process but I am not sure whether that would work or not, so I would really appreciate getting a second opinion on how to solve this little problem because it has been bugging me for weeks.
my Reducer:
import { FETCH_COMPANIES } from '../actions/types';
const initialState = {
next : null,
prev : null,
items : [],
item : [],
}
export default function(state = initialState, action) {
switch (action.type) {
case FETCH_COMPANIES:
return {
...state,
items : action.payload.companies,
next : action.payload.next,
prev : action.payload.prev,
}
default:
return state;
}
}
my Store.js:
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
compose(
applyMiddleware(...middleware)
)
)
export default store;
my Actions:
import axios from 'axios';
import { FloatType } from 'three';
import { FETCH_COMPANIES } from './types';
export const fetchAllData = (url) => dispatch => {
fetch(url)
.then(res => res.json())
.then(
posts =>
dispatch({
type : FETCH_COMPANIES,
payload : FetchCall(posts),
})
)
}
function FetchCall(res) {
let next;
let prev;
try {
next = res.next;
}
catch(err) {
console.log(err)
}
try {
prev = res.previous;
}
catch(err) {
console.log(err)
}
const CompanyArray = Array()
res.results.map(element => {
axios.get(`https://API/${element.symbol}/`).then((res) => {
const DataGroup = handleChartData(res.data)
CompanyArray.push({
'name' : element.symbol,
'data' : DataGroup,
})
})
});
const ALL_DATA = {
'next' : next,
'prev' : prev,
'companies' : CompanyArray,
}
return ALL_DATA;
}
function handleChartData(data) {
DataGroup = Object()
return DataGroup;
}
And my Component:
import React, { useState} from 'react';
import { Card, Row, Col, Button } from 'antd';
import Chart from '../components/Chart';
import DetailCompany from './CompanyDetail';
import { connect } from 'react-redux';
import { fetchAllData } from '../actions/chartActions';
import PropTypes from 'prop-types';
class CompanyList extends React.Component {
constructor(props) {
super(props);
this.state = {
charts : this.props.charts
}
}
componentWillMount() {
try {
this.props.fetchAllData("https://API/company/")
}
catch(err) {
console.log(err)
}
};
prevPage = () => {
let toPage = this.props.prev
this.props.fetchAllData(toPage)
}
nextPage = () => {
let toPage = this.props.next
this.props.fetchAllData(toPage)
}
render() {
const chartItems = this.state.charts.map(chart => (
<Col style={{margin:'0 0 75px 0'}} span={12} key={chart.name}>
<h1 style={{lineHeight:'2em', margin:'0 0 0 70px'}}>{chart.name}</h1>
<div className="chart-block">
<Chart symbol={chart.name}
data={chart.data.chartData}
>
</Chart>
</div>
</Col>
));
return (
<Card>
<Row>
{chartItems}
</Row>
<Row>
<Button disabled={(this.props.prev ? false : true )} onClick={() => {this.prevPage()}}>Previous</Button>
<Button onClick={() => {this.nextPage()}}>Next</Button>
</Row>
</Card>
)
}
}
CompanyList.propTypes = {
fetchAllData : PropTypes.func.isRequired,
charts : PropTypes.array.isRequired,
}
const mapStateToStore = state => ({
prev : state.charts.prev,
next : state.charts.next,
charts : state.charts.items,
});
export default connect(mapStateToStore, { fetchAllData })(CompanyList);
I would genuinely appreciate if anyone could help me to get around this problem and understand it to prevent further misdirection or reoccurrence. Thank you.
Your fetch thunk is not quite right. In particular, this line:
payload : FetchCall(posts),
FetchCall is asynchronous, but you aren't waiting for it to finish before dispatching. Within the FetchCall you are returning ALL_DATA with an empty CompanyArray before the axios calls finish.
You need to complete all fetch calls before returning or dispatching anything. You can do this with Promise/then, but I find it easier with async/await. Either way you need Promise.all to resolve the entire array. (Also I don't know why you use axios in one place and fetch in the other?).
// helper function to fetch the data for one company
const getCompanyData = async (symbol) => {
const res = await axios.get(`https://API/${symbol}/`);
return {
name: symbol,
data: res.data,
}
}
export const fetchAllData = (url) => async (dispatch) => {
const res = await axios.get(url);
const posts = res.data;
const {next, prev, results} = posts;
const companyArray = await Promise.all(
results.map( element => getCompanyData(element.symbol) )
);
dispatch({
type : FETCH_COMPANIES,
payload: {
next,
prev,
companyArray,
}
});
}
One of the issue that I noticed that the fetchcall is an async request so I think the companyarray would be a blank array in the payload. Are you sure you are getting payload.companies when FETCH_COMPANIES is dispatched?

Async redux action to fetch data is causing component to reload and cause react to react max depth in reload

I am trying to create a component that allows detecting changes in the redux store. Once the shouldUpdateData flag is set in the store, the component responsible for updating should fetch the data by using an async action creator. In my case, either the error "Maximum updates have reached" occurs or the update never happens.
Depending on the dispatch function stopFetching() (turns off the shouldUpdateData flag), the error or outcome changes. If I do the dispatch inside the action creator there are endless updates. If the code is used as it is below, no update occurs.
The reason I used the useSelector() hook from 'react-redux' is to detect a change in the store for the loading attribute.
Thank you in advance.
Here is the action creator:
export function updateDataAsync(id) {
return function (dispatch) {
// dispatch(fetchDataRequest());
return fetch(`/api/user/${id}/data`, {
method: "GET",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(
(result) => {
let {projects, notes} = result;
// New data and dispatch function
dispatch(fetchDataSuccess({projects, notes}));
},
(error) => { dispatch(fetchDataFailure(error)) }
)
}
}
Here is the reducer for this action creator:
export function savedData(state = DATA_INITIAL_STATE, action) {
switch(action.type) {
case FETCH_STATES.FETCH_DATA_REQUEST:
return {
...state,
loading: true
}
case FETCH_STATES.FETCH_DATA_SUCCESS:
return {
loading: false,
data: action.data,
error: ''
}
case FETCH_STATES.FETCH_DATA_FAILURE:
return {
loading: false,
data: {},
error: action.error.message
}
default:
return state;
}
}
The React component that is doing the update:
function StoreUpdater({ update, userId, shouldUpdate, startFetch, stopFetch, children }) {
const loading = useSelector(state => state.savedData.loading);
let reqSent = useRef(false);
useEffect(()=>{
if(!reqSent && shouldUpdate) {
startFetch();
update(userId)
reqSent.context = true;
}
})
return loading ? <LoadingAnimation /> : children;
}
const mapStateToProps = (state) => {
return {
userId: state.user.id,
shouldUpdate: state.shouldUpdateData // The flag that should trigger the update
}
}
const mapDispatchToProps = (dispatch) => {
return {
stopFetch: () => { dispatch(setShouldFetchData(false)) },
update: (id) => { dispatch(updateDataAsync(id)) },
startFetch: () => dispatch(fetchDataRequest()),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(StoreUpdater);
You dint pass any dependency to useEffect so it will be called on every render which is causing infinite renders
change useEffect to
useEffect(()=>{
if(!reqSent && shouldUpdate) {
startFetch();
update(userId)
reqSent.context = true;
}
},[])
For complete information regarding useEffect refer this link
The reference I created inside the component responsible of the updates, was causing the problem. The reference was preventing the update dispatch to occur due to the if statement being false.
mapStateToProps and mapDispatchToProps were react-redux higher order functions to connect classes components into the store. there equalants at functional components are useSelector and useDispatch. re-write your HOC redux adaption into hooks, and add [ dependency ] at useEffect usage
function StoreUpdater({ update, userId, shouldUpdate, startFetch, stopFetch, children }) {
const loading = useSelector(state => state.savedData.loading);
const userId = useSelector(state => state.user.id);
const shouldUpdate = useSelector(state => state.shouldUpdateData);
let reqSent = useRef(false);
const dispatch = useDispatch() // import from 'react-redux'
useEffect(()=>{
if(!reqSent && shouldUpdate) {
dispatch(startFetch());
dispatch(update(userId));
reqSent.context = true;
}
}, [reqSent, shouldUpdate, startFetch, dispatch, update, userId])
return loading ? <LoadingAnimation /> : children;
}
export default StoreUpdater ;

Awaiting Redux Action w/ React Hooks

I'm trying to handle a form submission to show a loading component when the data fetch is occuring. I'd like to display the data when it's been loaded into my Redux store.
Right now, I've set up my component to use React hooks. While the data loads into my redux store successfully, I'm not sure how to "await" the result of the action being completed. Here's a simplified version of what my component looks like:
const DataPage = (props) => {
const [isLoading, setIsLoading] = useState(false);
const [isError, setError] = useState(false);
useEffect(() => { // Reset Filters when dataSource changes...
setError(false);
setIsLoading(false);
}, [dataSource]);
const handleSubmit = (e, { dataSource }) => {
e.preventDefault();
setError(false)
setIsLoading(true);
//// IDEALLY THIS IS WHERE THE FIX WOULD GO? TURN THIS INTO ASYNC/AWAIT?
props.fetchData({ dataSource, token: localStorage.JWT_TOKEN });
};
return (
<div className="dataPage">
<form className="dataPage__filters" onSubmit={(e) => handleSubmit(e, { dataSource })}>
<DataSelector dataSource={dataSource} setDataSource={setDataSource}/>
<button className="button">
Search
</button>
</form>
{isError && <div>Something went wrong...</div>}
{ isLoading ? ( <div>...Loading </div> ) : (
<div className="dataPage__table">
<DataTable /> // This is connected to my redux-store separately through 'connect'
</div>
)}
</div>
);
};
const mapDispatchToProps = (dispatch) => ({
fetchData: ({ dataSource, token }) => dispatch(startFetchData({ dataSource, token }))
});
export default connect(null, mapDispatchToProps)(DataPage);
The relevant actions (startFetchData, and setData) are located in another file, and look like this:
export const setData = (data) => ({
type: "SET_DATA",
data
});
export const startFetchData = ({ dataSource, filter, filterTarget, token }) => {
return (dispatch) => {
axios.get(`${'http://localhost:8081'}/api/${dataSource}`, { headers: { authorization: token }})
.then((res) => {
dispatch(setData(result));
});
}
};
I'd like to be able to do this without introducing any new dependencies if possible.
A note for those using TypeScript: If you want to await a promise returned by an action using useDispatch() you may see TypeScript complaining about an unnecessary await.
In this case make sure to add the correct typing (see ThunkDispatch) to useDispatch via generics.
Also with useEffect() with async-await syntax make sure to wrap your async code in another closure because useEffect() expects a void return value and Typescript otherwise complains about you returning a Promise.
const dispatch = useDispatch<ThunkDispatch<any, any, Action>>();
useEffect(() => {
(async () => {
const myResult = await dispatch(...);
const anotherResult = await dispatch(...);
// ...
})();
});
I recommend you to use redux-thunk middleware. It's really easy and useful library to able your action to be, instead of objects, functions (including async functions). I'll give you an example:
Store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
import api from './services/api';
// Note: this API requires redux#>=3.1.0
const store = createStore(
rootReducer,
// With extra argument, in this case, my API):
applyMiddleware(thunk.withExtraArgument(api));
);
AuthDuck.js
Giving this duck (types, actions and reducers in the same file, see more here)
// ----------------------------------------------------------------
// Types
// ----------------------------------------------------------------
const Types = {
SIGN_IN_START: 'SIGN_IN_START',
SIGN_IN_SUCCESS: 'SIGN_IN_SUCCESS',
SIGN_IN_FAIL: 'SIGN_IN_FAIL'
};
// ----------------------------------------------------------------
// Actions
// ----------------------------------------------------------------
const signin = function (user) {
// LOOK HERE!
// Redux Thunk able you to return a function instead of an object.
return async function (dispatch, getState, api) {
try {
dispatch({ type: Types.SIGN_IN_START });
const token = await api.access.signin(user);
dispatch({ type: Types.SIGN_IN_SUCCESS, payload: token });
} catch (error) {
dispatch({ type: Types.SIGN_IN_FAIL, payload: error });
}
};
};
export const Actions = { signin };
// ----------------------------------------------------------------
// Reducers
// ----------------------------------------------------------------
export default function reducer(state, action) {
switch (action.type) {
case VeasyCalendarTypes.SIGN_IN_START:
return { ...state, isLoading: true };
case VeasyCalendarTypes.SIGN_IN_SUCCESS:
return { ...state, isLoading: false, token: action.payload };
case VeasyCalendarTypes.SIGN_IN_FAIL:
return { ...state, isLoading: false, error: action.payload };
default:
return state;
}
};
I hope to helped you, let me know if it worked for your case :)
Best regards

ReactJS x Redux: Reducer not returning state values

Hi I'm new at React and Redux.
I'm met with a problem with the reducer while trying to fetch a user object from the database. But it seems like it is not returning the state to the correct place?
On my front end editProfile.js:
import { a_fetchUser } from '../../../actions/resident/actions_user';
class EditProfile extends Component {
componentDidMount() {
this.props.fetchProfile({ iduser: this.props.auth.user.iduser });
console.log(this.props.store.get('isProcessing')); // returns false
console.log(this.props.store.get('retrievedUser')); // returns empty object {} when it's supposed to return data
}
// code simplified...
const mapStateToProps = state => ({
store: state.r_fetch_user,
auth: state.authReducer
});
const mapDispatchToProps = (dispatch, store) => ({
fetchProfile: (user) => {
dispatch(a_fetchUser(user));
}
});
export const EditProfileContainer = connect(
mapStateToProps,
mapDispatchToProps,
)(EditProfile);
}
Action actions_user.js:
import axios from 'axios';
const startFetchUser = () => ({
type: 'START_FETCH_USER',
});
const endFetchUser = response => ({
type: 'END_FETCH_USER',
response,
});
export const a_fetchUser = (user) => (dispatch) => {
dispatch(startFetchUser());
return axios.post('/rdb/getUser/', user)
.then((res) => {
console.log(res);
dispatch(endFetchUser(res));
})
.catch((err) => {
console.log(err);
dispatch(endFetchUser({ status: 'error' }));
});
};
Reducer userReducer.js:
import Immutable from 'immutable';
export const fetchUserState = Immutable.Map({
isProcessing: false,
feedbackType: null,
feedbackMsg: null,
retrievedUser: {},
});
export const r_fetch_user = (state = fetchUserState, action) => {
switch (action.type) {
case 'START_FETCH_USER':
console.log('start'); // printed
return state.set('isProcessing', true);
case 'END_FETCH_USER':
if (action.response.data.status === 'success') {
console.log(action.response.data.data[0]); // data retrieved from database successfully
return state.set('isProcessing', false).set('retrievedUser', action.response.data.data[0]);
} else {
return state.set('isProcessing', false).set('retrievedUser', {});
}
default:
return state;
}
};
My aim is to retrieve the object retrievedUser from the store. I've tried to console.log(this.props.store) on the front end and it did return a Map of the initial state, fetchUserState.
I've also tried to state.set (without returning) and it was successful so I came to a conclusion that there was something wrong with the return statement?
Additional details:
Using MERN stack.
this looks wrong:
const mapDispatchToProps = (dispatch, store) => ({
fetchProfile: (user) => {
dispatch(a_fetchUser(user));
}
});
What you need to do is to use bindActionCreators with, you can see example here and here:
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch)
}
or you can also change the syntax to:
const mapDispatchToProps = (dispatch) => ({
fetchProfile: a_fetchUser(user);
});
I am not sure what exactly your state.set() method does (in reducer) but if its mutating the state, then your reducer will not remain PURE function since its changing the original state obj. So please update below reducer method to start returning new state obj which should not mutate existing state obj:
export const r_fetch_user = (state = fetchUserState, action) => {
switch (action.type) {
case 'START_FETCH_USER':
console.log('start'); // printed
return state.set('isProcessing', true);
case 'END_FETCH_USER':
if (action.response.data.status === 'success') {
console.log(action.response.data.data[0]); // data retrieved from database successfully
return state.set('isProcessing', false).set('retrievedUser', action.response.data.data[0]);
} else {
return state.set('isProcessing', false).set('retrievedUser', {});
}
default:
return state;
}
};

Categories

Resources