7

I'm creating a React/Redux front-end for a multi-channel chat app. I'm having problems getting some React components to re-render after state change while using redux, react-redux, and redux-thunk.

I believe that my reducers are non-mutating, and that I'm subscribed via react-redux's connect. When I run the app and view the browser console, I see the initial render of the component (i.e. with initial, empty state), then the state change (triggered by an action dispatch in index.js).... I would then expect the component to re-render with new props, but it doesn't happen.

console log

I've put up a repo here: https://github.com/mattmoss/react-redux-no-update

node_modules is not in the repo, so to run, first download dependencies (running yarn is sufficient), then npm start.

Some excerpts (see full source in repo):

reducers/channelList.js

import * as c from '../actions/constants';

export default function channelList(state = [], action) {
    switch (action.type) {
        case c.FETCH_CHANNELS_SUCCESS:
            return action.channels;
        default:
            return state;
    }
}

actions/channelActions.js

export function fetchChannels() {
    return (dispatch) => {
        return ChannelApi.allChannels()
            .then(channels => dispatch(fetchChannelsSuccess(channels)))
            .catch(error => { throw(error); });
    };
}

export function fetchChannelsSuccess(channels) {
    return {
        type: c.FETCH_CHANNELS_SUCCESS,
        channels
    };
}

components/ChannelListView.js

class ChannelListView extends React.Component {
    render() {
        const { channels, current, onSelect } = this.props;

        console.log("channels:", channels, "current:", current);

        return (
            <ListGroup>
                {channels.map(channel =>
                    <ListGroupItem
                        key={channel.id}
                        active={channel.id === this.props.current}
                        onClick={onSelect(channel.id)}
                    >
                        <strong>#{channel.name}</strong>
                    </ListGroupItem>
                )}
            </ListGroup>
        );
    }
}

export default ChannelListView;

containers/ChannelList.js

import ChannelListView from '../components/ChannelListView';

const mapStateToProps = (state, ownProps) => {
    return {
        channels: state.channelList,
        current: state.currentChannel
    };
};

const mapDispatchToProps = (dispatch) => {
    return {
        onSelect: (id) => () => {}
    };
};

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

App.js

class App extends Component {
  render() {
    return (
      <Grid>
        <Row>
          <Col>
            <h1>Channels</h1>
            <ChannelList />
          </Col>
        </Row>
      </Grid>
    );
  }
}

index.js

const store = configureStore();
store.dispatch(fetchChannels());

ReactDOM.render(
    <Provider store={configureStore()}>
        <App />
    </Provider>,
    document.getElementById('root')
);

store/configureStore.js

import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers/rootReducer';
import thunk from 'redux-thunk';
import logger from 'redux-logger';

export default function configureStore() {
    return createStore(
        rootReducer,
        applyMiddleware(thunk, logger)
    );
}
Community
  • 1
  • 1
Matthew Moss
  • 1,223
  • 7
  • 16

1 Answers1

5

I'm not 100% as I am still relatively new to React myself. But look at your index.js script.

// You configure the store, then dispatch the fetchChannels action
const store = configureStore();
store.dispatch(fetchChannels());

ReactDOM.render(
    // But here, you're recreating the store again, which I think will re-initialise an empty store
    // Change this to use the `store` variable from above.
    <Provider store={configureStore()}>
        <App />
    </Provider>,
    document.getElementById('root')
);
fubar
  • 14,318
  • 3
  • 29
  • 34
  • good spotting... I missed this and was getting ready to set up a jsfiddle to try to work this out. – Michael Peyper May 09 '17 at 23:13
  • That's it... You know, wearing trifocals, you'd think my "eight eyes" would have seen this, but no.... In the end, I figured it would be something silly. Thanks a bunch! – Matthew Moss May 09 '17 at 23:14
  • You're welcome. I couldn't see anything wrong with what you'd written either until I spotted this. :) – fubar May 09 '17 at 23:19