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.
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)
);
}