React components unmount and remount when dispatching redux action in lifecycle methods
Asked Answered
C

1

12

This occurs with React + Redux Saga application my team is working on

We buy the React Isomorphic theme from Themeforest which bundles the Redux, Saga, and React Router V.4. We are now working on top of it.

I have been using React for a while but new to Redux and never experienced with such behavior. The problem is that whenever I dispatch an action, the component unmounts and remounts as the state changes

Okay, what I am doing is to fetch some user data from the API but. So this is how I come up with the following action & reducer

// Actions.js

const UserActions = {
    FETCH_USER_REQUEST: 'FETCH_USER_REQUEST',
    FETCH_USER_SUCCESS: 'FETCH_USER_SUCCESS',
    fetch_users: () => {
        return {
            type: UserActions.FETCH_USER_REQUEST
        }
    }
}

export default UserActions;

And the reducer

// Reducer.js
export default function UserReducer(state = initialState, action) {
    switch (action.type) {
        case 'REQUEST_USER_SUCCESS':
            return state.set('user_list', action.user_list);
        default:
            return state;
    }
}

With Redux Saga, the middleware is created to handle async actions. This is how it looks

// Saga.js
import { all, takeEvery, call, put, fork } from 'redux-saga/effects';
import {get, post} from 'axios';

export function fetchUser() {
    return get('https://mockapi.testapp.dev/users')
    .then(response => {
         return response.data;
    }).catch(error => {
         return error.data
    });
}

export function* fetchUserRequest() {
    yield takeEvery('FETCH_USER_REQUEST', function*() {
        const resp = yield call(fetchUser);
        yield put({
            action: 'FETCH_USER_SUCCESS',
            user_list: resp
        });
    });
}

export default function* rootSaga() {
   yield all([
      fork(fetchUserRequest)
   ]);
}

Now I implement the code in my component like this

// App.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import UserActions from './actions/UserAction';

const mapStateToProps = (state, ownProps) => {
    return {
        userList: state.User.get('user_list')
    }
}

const mapDispatchToProps = dispatch => {
    return {
        fetchUser: () => dispatch(UserActions.fetch_users())
    }
}

class App extends Component {
    componentDidMount() {
        this.props.fetchUser();
    }

    render() {
        // ... The rest of the rendering processes
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(App);

Now that you can see, with the behavior mentioned prior to this. Dispatching an action via this.props.fetchUser() cause the state to change but what I don't expect is that the component shouldn't unmount and remount because once it does so, an infinite loop occurs because the componentDidMount runs over and over and state also changes accordingly.

What I expect is to fetch the data from the API once the component mounts without remounting itself once the state changes for any reason because the theme we purchased is equipped with other base components which make use of Redux-saga to handle state and async actions. For example, the collapsable sidebar triggers a dispatch which changes the state that controls its bahavior once the users click on the it. Currently, once it does that my current component immediately unmounts unexpectedly.

Is there any possible way to solve such a problem or this is the default behavior of Redux?

Curtilage answered 30/1, 2018 at 4:25 Comment(8)
Have you tried using componentWillMount instead of componentDidMount. Also the component would remount if when rendering the <App> component you added a key that is dynamic such as <App key={newestUser}. Remounting could also occur if the parent component of App only sometimes renders App. If the parent occasionally doesn't render anything then you could try giving giving App a static key like <App key="main-app" /> Changing routes remounts components, if you could share your route file maybe there's something there I could find.Budd
I meet this problem with in my React Native projectWeise
same for me, any updates?Hourihan
I put your code and put it to codesandbox and I can't reproduce the problem: codesandbox.io/s/lpl7x everything works as it should (I had to fix some typos which I assume get there while writing the question)Philbin
@Chatthana Janethanakarn - Can you show your render function? it will be helpful for understanding the entire flow, thanks :)Calicut
the strange thing that I see is that you have state.set that probably mean that the sate is not a plain object, wich is discuraged as per redux documentation - redux.js.org/style-guide/style-guide#do-not-mutate-state - redux.js.org/style-guide/… - please use { usersList:[] }Juli
also why are you using UserActions = { FETCH_USER_REQUEST: 'FETCH_USER_REQUEST', FETCH_USER_SUCCESS: 'FETCH_USER_SUCCESS', ... } if you don't use UserActions.FETCH_USER_REQUEST ?Juli
I am also facing the same issue of mounting and remounting do we have any update on this issue?Residuum
G
0

using StrictMode in your application : when the component is first mounted: strictmode will simulate mounting, unmounting and remounting to detect any hidden bug in the code. It's a development tool to try to help you find bugs in your code. like unclean side effects in the use effects.It will run the useEffects twice. It will also detect deprecated code and show you a warning in the console.

It's a development tool that helps you find bugs in your code related to double rendering or forgetting to clean up your side effects.

You may wanna try uncommenting strictMode if you are using it. Let's see if it will solve the issue

Guinna answered 29/9, 2023 at 21:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.