Reputation: 148
I'm using React with Redux on my front end and using the Rails API to handle my backend. At present, I am trying to update a list of articles based on user addition of an article. The ArticleForm component fires an action creator that is successfully updating my ArticleList. However, at present the life cycle method componentWillUpdate is firing continuously making axios requests to Rails, and Rails keeps querying my database and sending back the articleList.
Note: I have tried using shouldComponentUpdate as such to no avail, the DOM doesn't update:
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
My question is: how can I use React's lifecycle methods to avoid this from happening and only happening when my articleList updates. Am I going down the wrong path using lifecycle methods? I'm fairly new to React/Redux so any and all advice is helpful!
I have the following container:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import ArticleForm from './ArticleForm'
import ArticleList from './ArticleList'
import removeArticle from '../actions/removeArticle'
import fetchArticles from '../actions/fetchArticles'
import updateArticleList from '../actions/updateArticleList'
class DumbArticleContainer extends Component {
componentWillMount() {
this.props.fetchArticles()
}
// shouldComponentUpdate(newProps){
// return newProps.articleList !== this.props.articleList
// }
componentWillUpdate(newProps){
if (newProps.articleList.articleList.count !== this.props.articleList.articleList.count){
this.props.updateArticleList()
}
}
render() {
return(
<div>
<ArticleForm />
<ArticleList articleList={this.props.articleList} />
</div>
)
}
}
const ArticleContainer = connect(mapStateToProps, mapDispatchToProps)(DumbArticleContainer)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles, updateArticleList}, dispatch);
}
export default ArticleContainer
here is the ArticleForm
import React, { Component, PropTypes } from 'react'
import { reduxForm } from 'redux-form'
import addArticle from '../actions/addArticle.js'
class ArticleForm extends Component {
constructor(props) {
super(props)
this.state = {disabled: true}
}
/* Most article elements are displayed conditionally based on local state */
toggleState(){
this.setState({
disabled: !this.state.disabled
})
}
handleFormSubmit(props) {
event.preventDefault()
const {resetForm} = this.props
this.props.addArticle(props).then( ()=>{
var router = require('react-router')
router.browserHistory.push('/dashboard')
resetForm()
})
}
render() {
const disabled = this.state.disabled ? 'disabled' : ''
const hidden = this.state.disabled ? 'hidden' : ''
const {fields: {title, url}, handleSubmit} = this.props;
return (
<div className="article-form">
<form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
<button className="article-form-btn"
hidden={!hidden}
onClick={this.toggleState.bind(this)}
>
+ Add Article
</ button>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Title"
{...title}
/>
<input className="article-form-input"
hidden={hidden}
type="textarea"
placeholder="Paste Link"
{...url}
/>
{ this.state.disabled
? ''
: <input className="article-form-input"
type="submit"
value="Save"
/>
}
</form>
</div>
);
}
}
export default reduxForm({
form: 'articleForm',
fields: ['title', 'url']
},
null,
{ addArticle })(ArticleForm);
and the ArticleList
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import removeArticle from '../actions/removeArticle.js'
import fetchArticles from '../actions/fetchArticles'
import { ListGroup } from 'react-bootstrap'
import { ListGroupItem } from 'react-bootstrap'
class Article extends Component {
render(){
var articleList = this.props.articleList.articleList
return(
<div>
<ListGroup>
{ articleList.slice(articleList.length - 10, articleList.length)
.map( (article) => {
return(
<ListGroupItem href="#" header={article.attributes.title}>
{article.attributes.url}
</ListGroupItem>
)}
)}
</ListGroup>
<div> View All Articles </div>
</div>
)
}
}
const ArticleList = connect(mapStateToProps, mapDispatchToProps)(Article)
function mapStateToProps(state) {
return {articleList: state.articleList}
}
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
export default ArticleList
action creator:
So here is my action creator import axios from 'axios'
import axios from 'axios'
function updateArticleList(){
const url = 'http://localhost:3000/api/v1/articles'
return axios.get(url).then( (response)=> {
return {
type: 'UPDATE_ARTICLE_LIST',
payload: response.data
}
})
}
export default updateArticleList
and reducer:
export default function articleReducer(state = {articleList: []}, action) {
switch(action.type){
case 'FETCH_ARTICLES':
return Object.assign({}, state, {articleList: action.payload.data});
case 'UPDATE_ARTICLE_LIST':
return Object.assign({}, state, {articleList: action.payload.data});
default:
return state
}
}
There is no issue with the store nor the action creators nor the reducers, they are all working pretty well. I can't really replicate the hundreds of queries rails is performing but am happy to include other code should anyone need to see it.
Thanks!
Upvotes: 3
Views: 1916
Reputation: 56
I'd like to keep a state called shouldUpdateList. Whenever I fire a action that changes the list(add or update an item to the list), I set shouldUpdateList to true. Then,set it back to false whenever I fire ajax action to fetch the list.
The lifecycle event I use to check shouldUpdateList is componentWillReceiveProps, if it's true I fire a fetch action.
EDIT: I mean keep shouldUpdateList state in Redux store. Something like:
const INIT_STATE = {
list: [],
shouldUpdateList: false
}
then
case Action.ADD_NEW:
//set shouldUpdateList to true
case Action.FETCH_LIST:
//set shouldUpdateList to false
lastly, in component
componentWillReceiveProps(nextProps) {
if(nextProps.shouldUpdateList === true) {
//dispatch action FETCH_LIST
}
}
Upvotes: 0
Reputation: 4375
Your mapDispatchToProps
is using bindActionCreators
wrong. Instead of
function mapDispatchToProps(dispatch) {
return {removeArticle: bindActionCreators({removeArticle}, dispatch),
fetchArticles: bindActionCreators({fetchArticles}, dispatch)
}
}
you should use
function mapDispatchToProps(dispatch) {
return bindActionCreators({removeArticle, fetchArticles}, dispatch);
}
bindActionCreators
can, as the name suggests, bind more than one action creator.
This probably won't solve your issue but an answer is the only place I could put this nicely.
Note that you'll need to fix how you're using it as well. No more double names.
Upvotes: 1