Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why didn't store.subscribe() fire after state got updated?

I'm new to react & redux so I made a simple page to get familiar with its concepts.

I just got this problem, store.subscribe() doesn't fire after I clicked the button once. However, it fires after I clicked twice. The more weird thing is when I clicked the button again, the function was triggered more than once.

Btw I'm not asking to correct my code (I know it sucks). I'm just wondering why my code have the strange behavior.

Let me just show the code:

/src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import store from './store';
import sendAction from './store/actionCreator'

class App extends React.Component {

    render() {
        return (
            <div>
                <button onClick={() => this.handleClick()}>Click me to send an action</button>
                <p>{store.getState().value}</p>
            </div>
        );
    }

    handleClick = () => {
        console.log('Before dispatch, the state is: ', store.getState());
        store.dispatch(sendAction());
        console.log('After dispatch, the state is: ', store.getState());
        store.subscribe(() => {
            console.log('subscribe triggered');
            this.setState({})
        });
    }
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
);

I will give you the Chrome console outputs below.

First time I clicked the button: enter image description here

Second time I clicked the button: enter image description here

More clicks: enter image description here

For completeness, here's the rest of the code but I believe they have nothing to do with the problem.

/src/store/actionCreator.js

export const SEND_MESSAGE='SEND_MESSAGE';
export default function sendAction() {
    return {
        type: SEND_MESSAGE,
        value: 'changed',
    };
}

/src/store/reducer.js

import {SEND_MESSAGE} from './actionCreator';
const initialState={
    value: 'default text',
};

export default (state=initialState, action) => {
    switch (action.type) {
        case SEND_MESSAGE: {
            return Object.assign({}, state, action);
        }
        default: {
            return state;
        }
    }
}

/src/store/index.js

import {createStore} from 'redux';
import reducer from './reducer';

const store=createStore(reducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
export default store;
like image 206
chiefVincent Avatar asked Nov 23 '25 20:11

chiefVincent


1 Answers

Issue

You are adding a new store subscription each time the handleClick handler is invoked.

  1. On the initial render you've yet to click the button and yet to subscribe to the store.
  2. 1st click - Action is dispatched. No subscription callback is active to log. A new subscription callback is set.
  3. 2nd click - Action is dispatched. 1st subscription callback logs. Another new subscription callback is set.
  4. 3rd click - Action is dispatched. 1st & 2nd subscription callbacks log. Another new subscription callback is set.
  5. Nth click - Action is dispatched. N-1'th subscription callbacks log. Another new subscription callback is set.

Solution

Establish the subscription once when the component mounts using the componentDidMount lifecycle method. Don't forget to unsubscribe from the store when the component unmounts.

class App extends React.Component {
  unsubscribe = null;

  componentDidMount() {
    this.unsubscribe = store.subscribe(() => {
      console.log('subscribe triggered');
      this.setState({})
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  handleClick = () => {
    console.log('Before dispatch, the state is: ', store.getState());
    store.dispatch(sendAction());
    console.log('After dispatch, the state is: ', store.getState());
  }

  render() {
    return (
      <div>
        <button onClick={() => this.handleClick()}>Click me to send an action</button>
        <p>{store.getState().value}</p>
      </div>
    );
  }
}
like image 133
Drew Reese Avatar answered Nov 25 '25 08:11

Drew Reese



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!