0

I'm building a small application in ReactJS, it consists of a grid of buttons with letters as values, what I need to do, is to fill an input field with the letters of the buttons clicked, basically like a keyboard.

I've built the grid with the buttons, each button has a letter, but I'm not sure on how I should code the next part; each button should have two stated, either clicked or not, if its clicked, the letter will appear on the input, if clicked again, it should be removed.

These are my components right now:

Square

import React from "react"

class Square extends React.Component {

    render() {
        return (
                <button type="button" className="square">{this.props.letter}</button>
        );
    }
}

export default Square;

Input Component

import React from 'react';

class Clear extends React.Component {
    render() {
        return (
            <div className="clear-btn">
                <button><span>Clear Word</span><span className="cross-icon">X</span></button>
                <input className="cust-input" type="text"/>
            </div>
        );
    }
}

export default Clear;

Main App Component

function App() {
  return (
    <div className="App">
      <div className="container">
        <div className="letters">
          {LettersJSON.board.map( (letter, index) => <Square key={index} letter={letter}/>)}
        </div>
        <div className="controls">
          <Clear />
        </div>
      </div>
    </div>
  );
}

export default App;

If anyone can help me on this it would be great, I don't know what would be a good way to get the value of the button and adding it on the input when clicked.

I imagine this would have to be done with events or something like that, quite honestly I'm just starting to learn React and I'm not sure on how I should arrange all the components so they work together.

This is how the app looks as of now:

enter image description here

3
  • Do you care if the button is clicked more than once? As in can the letter only be used once? Commented May 5, 2019 at 0:00
  • I do; each button should be either clicked or unclicked (I'll change the looks of the button when clicked once), so yes, each letter can only be used once Commented May 5, 2019 at 0:04
  • 1
    gotcha :) about to submit an answer for ya. Commented May 5, 2019 at 0:09

2 Answers 2

1

Consider the following code, also here is the sandbox for you:

https://codesandbox.io/s/6xpzvpno1r

This is our App component. We will populate the buttons here and give each button its letter, passing it through props. We also give each Button component a state-updater function that will update the state of our App component.

import React from 'react'
import ReactDOM from 'react-dom'
import Button from './Button'
import Input from './Input'

class App extends React.Component {
  state = {
    letters: ['a', 'b', 'c', 'd', 'e'],
    value: '',
  }

  updateValue = letter => {
    console.log('ran')
    this.setState({
      value: this.state.value + letter,
    })
  }

  createButtons = () => {
    const letters = this.state.letters
    return letters.map(letter => (
      <Button letter={letter} updateValue={this.updateValue} />
    ))
  }

  render() {
    return (
      <div>
        {this.createButtons()}
        <Input value={this.state.value} />
      </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('root'))

Button component: here we keep call that state-updating function on click and keep track if it has been clicked before.

import React from 'react'

class Button extends React.Component {
  state = {
    clicked: false,
  }

  handleOnClick = () => {
    if (!this.state.clicked) {
      this.props.updateValue(this.props.letter)
      this.setState({
        clicked: true,
      })
    }
  }

  render() {
    return (
      <button onClick={this.handleOnClick} disabled={this.state.clicked}>
        {this.props.letter}
      </button>
    )
  }
}

export default Button

Lastly we have our Input component: which just consumes the value from the parent App component.

import React from 'react'

class Input extends React.Component {
  render() {
    return <input value={this.props.value} />
  }
}

export default Input

Let me know if this is helpful to you. I feel like this essentially provides the principles you need to get your code to work.

Sign up to request clarification or add additional context in comments.

6 Comments

This definitely helps a lot! I'm giving it a read to understand everything first :)
Awesome! Great, I'm also trying to think of some additional functionality that would be cool for your app.
Its a pretty basic app, then only things I need to do is code the "Clear Word" button and add some validation, but this really points me in the right direction
I see. i see. Yeah that clear word button is a tricky one, but it should definitely be a cool challenge. Let me know if you got any questions with it. :)
@IvanS95 just figured out how to get the clear word functionality to work. You can check out my sandbox again. it should have the updated code. I'd be happy to explain what's happening for ya.
|
1

Let's break what you want into steps:

  1. Clicking a component should send its letter to the parent component.
  2. That array of letters should be stored in the parent component
  3. The input's value should be the value of that array, but as a string.

1) For the Square component to be clickable, it needs an onClick handler. On click, we'll call a function that's passed into Square from the parent component:

import React from "react"
class Square extends React.Component {
  render() {
    const { handleClick, letter } = this.props;
    return (
      <button type="button" className="square" onClick={() => handleClick(letter)}>
        {this.props.letter}
      </button>
    );
  }
}
export default Square;

2) Main app controller needs a state property to store the letters that get clicked so we can keep track of them. We also need to pass these letters to the input component.

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      clickedLetters: [],
    };
  }

  saveClickedLetter(letter) {
    const { clickedLetters } = this.state;
    const cloneOfClickedLetters = clickedLetters;
    cloneOfClickedLetters.push(letter);
    this.setState({ clickedLetters: cloneOfClickedLetters });
  }

  render() {
    const { clickedLetters } = this.state;
    return (
      <div className="App">
        <div className="container">
          <div className="letters">
            {LettersJSON.board.map( (letter, index) => <Square key={index} letter={letter} handleClick={this.saveClickedLetter}/>)}
          </div>
          <div className="controls">
            <Clear clickedLetters={clickedLetters.length > 0 && clickedLetters.join()}/>
          </div>
        </div>
      </div>
    )
  }
}
export default App;

Finally, let's pass in the clickedLetters prop to input's value attribute:

import React from 'react';
class Clear extends React.Component {
  render() {
    const { clickedLetters } = this.props;
    return (
      <div className="clear-btn">
        <button><span>Clear Word</span><span className="cross-icon">X</span></button>
        <input value={clickedLetters} className="cust-input" type="text"/>
      </div>
    );
  }
}
export default Clear;

4 Comments

I'm going to try this out on my code, I just want to grasp everything to know how its all working, thank you!
I have a question, in this case, the onClick={() => handleClick(letter)} attribute on the Square component is giving me an error because it says letter is not defined, should this be this.props.letter instead?
Correct! let me edit my original post. I forgot to include letter in the destructuring.
Great, I made the change, but now it says TypeError: Cannot read property 'state' of undefined referring to the App component, any idea why is that? Should the state object be explicitly defined there?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.