albert
albert

Reputation: 601

My component isn't updating, am I mutating the state?

I have a container component that connects to the state which I made with immutable.js. When I update the state, my redux inspector tells me that the state is updated, but my component doesn't get the new updates and doesn't re-render.

My container component:

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { setCategoryActive } from '../actions'
import Category from '../components/Category'

class MenuList extends Component {


  constructor(props) {
    super(props)
    this.categories = this.props.categories
    this.subreddits = this.props.subreddits
    this.allCategories = this.categories.get("allCategories")
    this.byName = this.categories.get("byName")
  }

  printSubreddits(category) {
    const subreddits = this.byName.get(category)
    const subredditsByName = subreddits.get("subredditsByName")

    const list = subredditsByName.map((subreddit, i) => {
      return <p className="swag" key={i}>{subreddit}</p>
    })

    return list
  }

  isCategoryActive(category) {
    const cat = this.byName.get(category)
    return cat.get("active")
  }

  printCategory(category, i) {
    console.log(this.isCategoryActive(category))
    return (
      <div className="category-container" key={i}>
        <Category name={category}
          active={this.isCategoryActive(category)}
          setActive={this.props.setCategoryActive.bind(this, category)} />
        {this.isCategoryActive(category) ? this.printSubreddits(category) : null}
      </div>
    )
  }


  render() {

    return (
      <div>
        {this.allCategories.map((category, i) => {
          const x = this.printCategory(category, i)
          return x
        }, this)}
      </div>
    )
  }
}

const mapStateToProps = (state) => ({
  categories: state.subredditSelector.get('categories'),
  subreddits: state.subredditSelector.get('subreddits')
})

export default connect(mapStateToProps, {
  setCategoryActive
})(MenuList);

My Category component

class Category extends Component {

  printToggle(active) {
    if (active) {
      return <span> [-]</span>
    } else {
      return <span> [+]</span>
    }
  }

  componentWillReceiveProps(nextProps) {
    this.printToggle(nextProps.active)
  }

  render() {

    const { setActive, active, name } = this.props

    return (
      <div className="category-container">
        <a onClick={setActive}
        href="#"
        className="category-title">
          {name}
          {this.printToggle(active)}
        </a>
      </div>
    )
  }

}

export default Category

And my reducer

import { fromJS } from 'immutable'
import {
  SET_SUBREDDIT_ACTIVE,
  SET_CATEGORY_ACTIVE
} from '../actions'
import List from '../data/nsfw_subreddits.js'

const initState = fromJS(List)

const subredditSelector = (state = initState, action) => {
  switch (action.type) {
    case SET_SUBREDDIT_ACTIVE:
      return state
    case SET_CATEGORY_ACTIVE:
      return state.updateIn(['categories', 'byName', action.payload.name],
        x => x.set('active', !x.get('active')))
    default:
      return state
  }
}

export default subredditSelector

A piece of my state that I have coded as a JSON object

const list = {
  categories: {
    byName: {
      "example": {
        name: "example",
        id: 1,
        active: true,
        subredditsByName: ["example", "example"]
      },
      "example": {
        name: "example",
        id: 2,
        active: true,
        subredditsByName: ["example"]
      },
      "example": {
        name: "example",
        id: 3,
        active: true,
        subredditsByName: ["example", "example", "example"]
      }
    },
    allCategories: ["example", "example", "example"]
  },
  subreddits: {

My guess is that my reducer is mutating the state? Though I am not sure how, since I am using the immutable js functions?

Upvotes: 0

Views: 92

Answers (2)

Frazer Kirkman
Frazer Kirkman

Reputation: 1154

You should still keep your constructor, but only have in it what needs to be done when the object is first created:

constructor(props) { super(props) }

And yes, having

cleanPropData(){ this.categories = this.props.categories this.subreddits = this.props.subreddits this.allCategories = this.categories.get("allCategories") this.byName = this.categories.get("byName") }

is fine. I haven't heard of someone invoking it at the top of a render function. Nice to know that works.

Upvotes: 0

albert
albert

Reputation: 601

So I fixed this issue by changing the constructor from a constructor to a regular function and called it at the top of my render method!

Upvotes: 1

Related Questions