Search code examples
javascriptreactjsreduxtic-tac-toe

Render Images in tic tac toe app using reactjs


I am trying to create a simple tic tac toe app using reactjs, with two modes in it: Classic and Image, in the classic mode I have the options to display X and O, while in the Image mode, I have the options two display two images which are mentioned below. My file structure is:

src
  components
    ChooseGameMode.js
    choosePlayer.js
    GameStatus.js
    Status.js
  images
    connery.svg
    square.svg
App.css
App.js
index.css
index.js
...

Following is the code that I developed:

App.js

import React, { Component } from 'react';

import './App.css';

import Status from'./components/Status';
import GameStatus from'./components/GameStatus';

class App extends Component {


  constructor(props){

    super(props)

    this.state = {

      board : Array(9).fill(null),
      player : null,
      winner : null,
      gamemode : null,
      /* array to store the ndex */
      order_ndex : []
    }

  }

  checkWinner(){

    let winLines =
      [
        ["0", "1", "2"],
        ["3", "4", "5"],
        ["6", "7", "8"],
        ["0", "3", "6"],
        ["1", "4", "7"],
        ["2", "5", "8"], 
        ["0", "4", "8"],
        ["2", "4", "6"]
      ]

    this.checkmatch(winLines)
  }

  checkmatch(winLines){
    let board = this.state.board;
    for (let index = 0; index < winLines.length; index++) {
      const [a,b,c]=winLines[index];
      if(board[a] && board[a] === board[b] && board[a] === board[c] ){
        alert('You won!');
        this.setState({
          winner : this.state.player
        })
        this.state.winner = this.state.player;
      }
    }
    if(!this.state.winner && !board.includes(null)){
      this.state.winner = 'None';
      alert('Its a Draw!');
    }
  }

  handleClick(index){

    //To render images on selecting ImageMode mode
    const images ={
      connery : require('./images/connery.svg'),
      square : require('./images/square.svg')
    }


    if(this.state.player && !this.state.winner && this.state.gamemode === "Classic"){

      let newBoard = this.state.board

      if(this.state.board[index]===null){

        newBoard[index] = this.state.player
        /* push the last index into the array */
        this.state.order_ndex.push(index) 
        this.setState({
          board: newBoard,
          player: this.state.player==="X" ? "O" : "X"
        })

        this.checkWinner()

      }
    }

    else{

      let newBoard = this.state.board

      if(this.state.board[index]===null){

        newBoard[index] = this.state.player
        /* push the last index into the array */
        this.state.order_ndex.push(index) 
        this.setState({
          board: newBoard,
          player: this.state.player=== images.connery ? images.square : images.connery
        })

        this.checkWinner()

      }

    }
  } 

  setPlayer(player){
    this.setState({player})

  }

  setGameMode(gamemode){
    console.log(gamemode)
    this.setState({gamemode})

  }

  renderBoxes(){
    return this.state.board.map(
      (box, index) => 
      <div className="box" key={index} 
        onClick={()=> {this.handleClick(index)}}>
        {box}
      </div>
    )
  }

  reset(){

    this.setState({
      board : Array(9).fill(null),
      player :  null,
      winner : null,
      gamemode : null,
      order_ndex : []

    })

  } 

  undo() {
    let ndex = this.state.order_ndex.pop() 
    let newBoard = this.state.board
    let prev = newBoard[ndex]
    newBoard[ndex] = null
    this.setState({
      board: newBoard,
      player: prev
    })
  }

  render() {


    return (

      <div className="container">
        <h1>Tic Tac Toe App</h1>

        <GameStatus 
          gamemode ={this.state.gamemode} 
          setGameMode = {(e)=> this.setGameMode(e)}
        />

        <Status
          player={this.state.player} 
          setPlayer={(e) => this.setPlayer(e)}
          winner = {this.state.winner}
        />

        <div className="board">

          {this.renderBoxes()}

        </div>
        <div className="btn">
          <button className='reset' onClick = {() => this.reset()}> Reset </button>
          <div className="divider"/>
          <button className='reset' disabled ={this.state.winner} onClick = {() => this.undo()}> Undo </button>
        </div>

      </div>

    );
  }
}

export default App;

ChooseGameMode.js

import React, { Component } from 'react';

class ChooseGameMode extends Component{

    handleForm(e){
        e.preventDefault();
        this.props.gamemode(e.target.gamemode.value);
    }

    render(){
        return (
            <form onSubmit={(e)=> this.handleForm(e)}>
                <label>
                    Classic
                    <input type="radio" name="gamemode" value="Classic"/>
                </label>

                <label>
                    Frontenddevlandia
                    <input type="radio" name="gamemode" value="Frontenddevlandia"/>
                </label>

                <input type="submit" value="Submit" />
            </form>
        )

    }
}

export default ChooseGameMode;

choosePlayer.js

import React, { Component } from 'react';

class Player extends Component{

    handleForm(e){
        e.preventDefault();
        this.props.player(e.target.player.value);
    }

    render(){
        return (
            <form onSubmit={(e)=> this.handleForm(e)}>
                <label>
                    Player X
                    <input type="radio" name="player" value="X"/>
                </label>

                <label>
                    Player O
                    <input type="radio" name="player" value="O"/>
                </label>

                <input type="submit" value="Start" />
            </form>
        )

    }
}

export default Player;

GameStatus.js

import React, { Component } from 'react';

import ChooseGameMode from'./ChooseGameMode';


class GameStatus extends Component {

    handleSetGameMode(e){
        this.props.setGameMode(e)
    }

    render(){

        return (this.props.gamemode ? 
              <h3>You are playing the {this.props.gamemode} mode</h3> : 
              <ChooseGameMode gamemode={(e) => this.handleSetGameMode(e)} />
        )
    }
}

export default GameStatus;

Status.js

import React, { Component } from 'react';

import Player from'./choosePlayer';

class Status extends Component {

    handleSetPlayer(e){
        this.props.setPlayer(e)
    }

    renderHtml(){
        if (this.props.winner){
            return (<h2>Winner is {this.props.winner}</h2>)
        } else {
            return this.props.player ? 
            <h2>Next player is {this.props.player}</h2> : 
            <Player player={(e) => this.handleSetPlayer(e)} />
        }
    }

    render(){

        return (<span>{this.renderHtml()}</span>)
    }
}

export default Status;

When I select the Image mode and choose the player instead of rendering the image, its simply rendering the path of the image file into the app. I used require('./images/connery.svg'), for rendering the image. May I know what am I doing wrong, also I haven't used Redux here for state management, as I am new to learning react and redux, can someone please help me on how to implement redux into this existing app, so that the state management can be handled in a better way rather than passing the states as individual props for different components? A general idea on how to implement this and any other improvements or suggestions will also be really helpful. Also, as suggested this is my codesandbox link.


Solution

  • To use images, you do not need to modify your classic code, you only need to update renderBoxes() in your working classic code and remove the conditions for Image that you added to other parts of the code e.g in App.js handleClick().

    const connery = require("./images/connery.svg"); // or import connery from "./images/connery.svg"
    const square = require("./images/square.svg");
    
      ...
      
      renderBoxes() {
        
        const isFrontend = this.state.gamemode === 'Image'
      
        return this.state.board.map((box, index) => (
          <div
            className="box"
            key={index}
            onClick={() => {
              this.handleClick(index);
            }}
          >
            {box === "X" && isFrontend && <img src={connery} />}
            {box === "O" && isFrontend && <img src={square} />}
            {!isFrontEnd && box}
          </div>
        ));
      }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

    To make your question less cumbersome next time IMO you can host your code at https://codesandbox.io/s and just show only the question here.