Reputation: 1066
I have a blog application that I want to sort my posts by votes or title, so I have this buttons for sorting:
<Button size='mini' onClick={() => {this.props.sortByVotes()}}>
Votes
</Button>
<Button size='mini' onClick={() => {this.props.sortByTitle()}}>
Title
</Button>
The actions is like this:
export const sortByVotes = posts => ({ type: SORT_BY_VOTES })
export const sortByTitle = posts => ({ type: SORT_BY_TITLE })
And reducer is as it follows:
case SORT_BY_VOTES:
return {
...state,
posts: state.posts.sort((a, b) => b.voteScore - a.voteScore)
}
case SORT_BY_TITLE:
return {
...state,
posts: state.posts.sort((a, b) => {
if (a.title > b.title) return 1
if (a.title < b.title)return -1
return 0
})
}
Finally, in the Main.js view I get posts in componentDidMount and shows it like this:
<Item.Group divided>
{this.props.posts.map((p, idx) =>
<PostSmall key={idx}
id={p.id}
title={p.title}
body={p.body}
category={p.category}
voteScore={p.voteScore}
/>
)}
</Item.Group>
Still in Main.js, I map the posts from state like this:
function mapStateToProps(state) {
return {
posts: state.posts.posts,
categories: state.categories.categories
}
}
As you can see, nothing special here.
The problem is: the state is update as expected, but view not.
Ti'll now I have not figured out how to solve it, and why this is happening.
Any help will be grateful.
Upvotes: 0
Views: 54
Reputation: 641
The object posts is not changing hence React is not rendering the component.
I have added a work around fix.
reducers/posts.js
case SORT_BY_VOTES:
const posts = Object.assign({},{posts:state.posts.sort((a, b) => b.voteScore - a.voteScore
)})
return Object.assign ({}, state, posts);
In Main.js
import React from 'react'
import { connect } from 'react-redux'
import PropTypes from 'prop-types'
import { Container, Divider, Grid, Item } from 'semantic-ui-react'
import Categories from '../components/Categories'
import PostSmall from '../components/PostSmall'
import PostsSorter from '../components/PostsSorter'
import { fetchPosts } from '../actions/posts'
import { fetchCategories } from '../actions/categories'
class Main extends React.Component {
render() {
const posts = this.props.posts ? this.props.posts.posts || [] : [];
console.log('render')
return (
<Container>
<Grid columns='equal'>
<Grid.Column>
<PostsSorter/>
<Divider/>
<Categories categories={this.props.categories}/>
</Grid.Column>
<Grid.Column width={10}>
<Item.Group divided>
{posts.map((p, idx) =>
<PostSmall key={idx} post={p}/>
)}
</Item.Group>
</Grid.Column>
</Grid>
</Container>
);
}
componentDidMount() {
this.props.getPosts()
this.props.getCategories()
}
}
Main.propTypes = {
posts: PropTypes.array,
categories: PropTypes.array
}
Main.defaultProps = {
posts: [],
categories: []
}
function mapStateToProps(state) {
console.log(state);
return {
posts: state.posts,
categories: state.categories.categories
}
}
function mapDispatchToProps(dispatch) {
return {
getPosts: () => dispatch(fetchPosts()),
getCategories: () => dispatch(fetchCategories())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Main)
But you have to refactor the code in order to make the component pure and connect the redux state only to view.
Upvotes: 1
Reputation: 2675
Your action doesnt send a payload. Are you initiating an API call in actions to send to the reducer? In your reducer, you need to capture the payload from actions and then update the state.
Actions: actions.tsx Reducers: reducers.tsx
So, the common workflow is to have a parameter in actions and then modify the default state in the reducer. See a sample above for reference.
Hope this helps.
Upvotes: 0