user3918161 user3918161 - 5 months ago 108
Node.js Question

Extracting properties from material-ui's GridList

I only started studying React a few days ago so please forgive me if this question sounds really stupid.

In this work assignment, I have to implement a 'Like' system using material-ui's GridList. There will be eight pictures in total where users can like them by clicking on the like button. In my current code, users can click on the like button but all the like buttons will be affected instead of just one. Furthermore, the number of likes does not increase.

So my question is, how can I change the number of likes when a user clicks the 'Like' button and make sure only 1 button is affected? I have tried props and even lodash but I just cannot seem to figure out the problem. Below is my entire code for the GridList portion. Any help would be greatly appreciated.

import _ from 'lodash';
import React from 'react';
import {GridList, GridTile} from 'material-ui/GridList';
import Subheader from 'material-ui/Subheader';
import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
import getMuiTheme from 'material-ui/styles/getMuiTheme';

//GridList style
const styles = {
root: {
display: 'flex',
flexWrap: 'wrap',
justifyContent: 'space-around',
},
gridList: {
width: 1000,
height: 500,
},
};

//data for the GridList
var tilesData = [
{
img: './images/image_01.jpg',
title: 'Breakfast',
likes: 0,
},
{
img: './images/image_02.jpg',
title: 'Tasty burger',
likes: 0,
},
{
img: './images/image_03.jpg',
title: 'Camera',
likes: 0,
},
{
img: './images/image_04.jpg',
title: 'Morning',
likes: 0,
},
{
img: './images/image_05.jpg',
title: 'Hats',
likes: 0,
},
{
img: './images/image_06.jpg',
title: 'Honey',
likes: 0,
},
{
img: './images/image_07.jpg',
title: 'Vegetables',
likes: 0,
},
{
img: './images/image_08.jpg',
title: 'Water plant',
likes: 0,
},
];

export default class Grid extends React.Component {
constructor(props){
super(props);
this.state = {
like: false,
likes: tilesData.likes,
};
this.post = this.post.bind(this);
this.delete = this.delete.bind(this);
}

//if Like button is clicked
post(){
this.setState({ like: true});
let likes = this.state.likes;
likes++;
this.setState({likes: likes});
//this.tilesData.likes = likes;
}

//if Like button is "unclicked"
delete(){
this.setState({ like: false});
let likes = this.state.likes;
likes--;
this.setState({likes: likes});
//this.tilesData.likes = likes;
}

getChildContext() {
return { muiTheme: getMuiTheme(baseTheme) };
}

render(){
const likeBtn = this.state.like ? <img src="./images/icons/icon_2.png" onClick={this.delete} /> : <img src="./images/icons/icon_1.png" onClick={this.post} />;
return (
<div style={styles.root}>
<GridList
cellHeight={200}
cols={4}
style={styles.gridList}
>
<Subheader>December</Subheader>
{tilesData.map((tile) => (
<GridTile
key={tile.img}
title={tile.title}
subtitle={<span>Likes: <b>{tile.likes}</b></span>}
actionIcon={likeBtn}
>
<img src={tile.img} />
</GridTile>
))}
</GridList>
</div>
);
}
}

Grid.childContextTypes = {
muiTheme: React.PropTypes.object.isRequired,
}

Answer

Problem

'State' of like button is defined outside the loop means same 'state' is shared with all GridTile components(for all images).

When you clicked on 'like' button then you are changing the 'state' of 'like' button in parent component that is Grid and same 'state' is used for all like buttons.

That's why it is impacting all like buttons.

Solution

'state' should be defined separately for each like button. Also delete and post method should be defined inside loop means in GridTile.

But GridTile is part of material-ui library so instead of changing this library create a wrapper on GridTile component. Grid component will call component lets say it GridTileCustom component inside loop.

Inside GridTileCustom component you need to define delete and post method which you are using in 'onClick' event So your final code will look like

import React from 'react';
  import {GridList, GridTile} from 'material-ui/GridList';
  import Subheader from 'material-ui/Subheader';
  import baseTheme from 'material-ui/styles/baseThemes/lightBaseTheme';
  import getMuiTheme from 'material-ui/styles/getMuiTheme';
  import IconButton from 'material-ui/IconButton';

  const thumbsIcon = "glyphicon glyphicon-thumbs-up";

  const styles = {
  root: {
    display: 'flex',
    flexWrap: 'wrap',
    justifyContent: 'space-around',
  },
  gridList: {
    width: 1000,
    height: 500,
  },
  };

  var tilesData = [
  {
    img: './images/image_01.jpg',
    title: 'Breakfast',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_02.jpg',
    title: 'Tasty burger',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_03.jpg',
    title: 'Camera',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_04.jpg',
    title: 'Morning',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_05.jpg',
    title: 'Hats',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_06.jpg',
    title: 'Honey',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_07.jpg',
    title: 'Vegetables',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  {
    img: './images/image_08.jpg',
    title: 'Water plant',
    likes: 0,
    icon: './images/icons/icon_1.png',
  },
  ];

  export default class Grid extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      like: false,
      likes: tilesData.likes,
    };
    // this.post = this.post.bind(this);
    // this.delete = this.delete.bind(this);
  }

  // post(){
  //     this.setState({ like: true});
  //     let likes = this.state.likes;
  //     likes++;
  //     this.setState({likes: likes});
  //     //this.tilesData.likes = likes;
  // }

  // delete(){
  //     this.setState({ like: false});
  //     let likes = this.state.likes;
  //     likes--;
  //     this.setState({likes: likes});
  //     //this.tilesData.likes = likes;
  // }

  getChildContext() {
    return { muiTheme: getMuiTheme(baseTheme) };
  }

  render(){
    return (
      <div style={styles.root}>
        <GridList
          cellHeight={200}
          cols={4}
          style={styles.gridList}
        >
        <Subheader>December</Subheader>
        {tilesData.map((tile) => (
          <GridTileInternal
            key={tile.img}
            img={tile.img}
            title={tile.title}
            subtitle={tile.likes}
            // actionIcon={likeBtn}
          >
          </GridTileInternal>
          ))}
        </GridList>
      </div>
    );
  }
  }

  class GridTileInternal extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      like: false,
      likes: tilesData.likes,
    };
    this.post = this.post.bind(this);
    this.delete = this.delete.bind(this);
  }

  post(){
      this.setState({ like: true});
      let likes = this.state.likes;
      likes++;
      this.setState({likes: likes});
      //this.tilesData.likes = likes;
  }

  delete(){
      this.setState({ like: false});
      let likes = this.state.likes;
      likes--;
      this.setState({likes: likes});
      //this.tilesData.likes = likes;
  }


  render(){
    const likeBtn = this.state.like ? <img src="./images/icons/icon_2.png" onClick={this.delete} /> : <img src="./images/icons/icon_1.png" onClick={this.post} />;
    return (
          <GridTile
            key={this.props.img}
            title={this.props.title}
            subtitle={<span>Likes: <b>{this.props.subtitle}</b></span>}
            actionIcon={likeBtn}
          >
            <img src={this.props.img} />
          </GridTile>
    );
  }
  }

  Grid.childContextTypes = {
  muiTheme: React.PropTypes.object.isRequired,
  }
Comments