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

2 Answers

1
yourfavoritedev On Best Solutions

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.

1
jcrowson On

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;