Kay
Kay

Reputation: 2332

Update other component on input value change React

I am learning React and I am trying to update one component based on an input value. I have done this using html and vanilla JavaScript and it works. I am trying to implement the same functionality using React but I am having some challenges.

Here is my code in vanilla javascript:

index.html

<!DOCTYPE html>
<html lang="en">

    <body>
        <div>
            <div>
                <div>
                    <h2>Press button when you are ready to play</h2>
                    <input type="text" placeholder="Start typing..." id="word-input" autofocus>
                </div>
            </div>
        </div>

        <script src="script.js"></script>
    </body>

</html>

script.js

const wordInput = document.querySelector('#word-input');
const currentWord = document.querySelector('#current-word');


const words = [
    'rock', 'paper', 'scissors', 'play'
];

displayWord(words)

wordInput.addEventListener('input', matchWords);

function displayWord(word) {
    const Index = Math.floor(Math.random() * word.length);
    currentWord.textContent = word[Index];
};

// match words
function matchWords() {

    if(wordInput.value === currentWord.innerHTML) {
        displayWord(words); 
        wordInput.value = ''; 
    };
};

Here is what I have tried in React so far:

import React, { Component, createRef } from 'react'

export class About extends Component {

    state = {
        words: [
            'rock', 'paper', 'scissors', 'play'
        ],
        current_input: 'Start...',
        current_word: 'break'
    }
    
    inputRef = createRef();

    displayWord = (word) => {

        const Index = Math.floor(Math.random() * word.length);
        let rword = word[Index];

        this.setState({
            current_word: rword
        })

    };
    
    matchWords = (e) => {
        
        this.setState({
            current_input: e.target.value
        })

        if(this.state.current_input === this.state.current_word) {

            this.displayWord(this.state.words); 

            this.setState({
                current_input: ''
            })

            this.inputRef.current.focus();
        };
    };

    render() {
        return (
                <div>
                    <div>
                        <div>
                            <h2 id="current-word">{this.state.current_word}</h2>
                            <input ref={this.inputRef}
                             placeholder='start ..' onChange={this.matchWords} type="text"  id="word-input" autoFocus />
                        </div>  
                    </div>
                </div>
        )
    }
}

export default About

It seems to work. However, the problem is it doesn't update after I type in the word - I have to attempt to delete the contents of the input box before I see a new word show up. Also, it doesn't automatically clear the contents and autofocus on the input box for me to type the new word. I would like to know what I am missing and also as far as best practices go, is this the best or 'right' way to do something like this using React. Thanks!

Upvotes: 0

Views: 1254

Answers (2)

w0lf
w0lf

Reputation: 383

class About extends Component {
  state = {
    tmp: "",
    words: ["rock", "paper", "scissors", "play"],
    current_input: "",
    current_word: "break"
  };

  inputRef = createRef();

  displayWord = (word) => {
    const Index = Math.floor(Math.random() * word.length);
    let rword = word[Index];

    this.setState({
      current_word: rword
    });
  };

  matchWords = (e) => {
    this.setState({ current_input: e.target.value }, () => {
      if (this.state.current_input === this.state.current_word) {
        this.displayWord(this.state.words);

        this.setState({
          current_input: ""
        });

        this.inputRef.current.focus();
      }
    });
  };

  render() {
    return (
      <div>
        <div>
          <div>
            <h2 id="current-word">{this.state.current_word}</h2>
            <input
              ref={this.inputRef}
              value={this.state.current_input}
              placeholder="start .."
              onChange={(e) => this.matchWords(e)}
              type="text"
              id="word-input"
              autoFocus
            />
          </div>
        </div>
      </div>
    );
  }
}

Because of asynchronous operations current_input state update without last character.
Refer this: onChange in React doesn't capture the last character of text
Working demo of your code : https://codepen.io/navindu_d/pen/OJROmxN

Upvotes: 1

thelonglqd
thelonglqd

Reputation: 1862

I think, you should change your code inside function matchWords like below:

  matchWords = (e) => {
    if (e.target.value === this.state.current_word) {
      this.displayWord(this.state.words);

      this.setState({
        current_input: ""
      });

      this.inputRef.current.focus();
    } else {
      this.setState({
        current_input: e.target.value
      });
    }
  };

It does not run immediately as you expect because setState runs asynchronously, when compare if (this.state.current_input === this.state.current_word) the current input is the old value. Like when you type break the comparison is if ('brea' === 'break')

Upvotes: 1

Related Questions