Learner
Learner

Reputation: 21393

React Change Username

I am working on a task to practice react programming, this is the task - Change Username

Here is the explanation:

This application should allow the user to update their username by inputting a custom value and clicking the button.

The Username component is finished and should not be changed, but the App component is missing parts. Finish the App component so that the Username component displays the inputted text when the button is clicked.

The App component should use the React.useRef Hook to pass the input to the Username component for the input element and for the Username component.

For example, if the user inputs a new username of "John Doe" and clicks the button, the div element with id root should look like this:

<div><button>Change Username</button><input type="text"><h1>John Doe</h1></div>

This is the code given:

class Username extends React.Component {
  state = { value: "" };

  changeValue(value) {
    this.setState({ value });
  }

  render() {
    const { value } = this.state;
    return <h1>{value}</h1>;
  }
}

function App() {
  function clickHandler() {}

  return (
    <div>
      <button onClick={clickHandler}>Change Username</button>
      <input type="text" />
      <Username />
    </div>
  );
}

document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

I tried a lot to understand how to solve this, but I am not able to fix this, how to solve this problem?

Upvotes: 1

Views: 5218

Answers (4)

N Djel Okoye
N Djel Okoye

Reputation: 1080

In the real world you would probably do something like this: -

import React, { useRef } from "react";
import ReactDOM from "react-dom";

class Username extends React.Component {
  state = { value: "" };

  changeValue(value) {
    this.setState({ value });
  }

  render() {
    const { value } = this.state;
    return <h1>{value}</h1>;
  }
}

function App() {
  const myRef = useRef();
  function clickHandler() {
    document.querySelector("h1").innerHTML = myRef.current.value;
  }

  return (
    <div>
      <button onClick={clickHandler}>Change Username</button>
      <input type="text" ref={myRef} />
      <Username />
    </div>
  );
}

document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

document.querySelector("input").value = "John Doe";
document.querySelector("button").click();
setTimeout(() => console.log(document.getElementById("root").innerHTML));

Upvotes: 0

JMRC
JMRC

Reputation: 1514

This works

import React from 'react'
import ReactDOM from 'react-dom'

class Username extends React.Component {
  constructor(props){
    super(props);
    this.state = {value: ""};
  }

  changeValue(value) {
    this.setState({value: value});
  }

  render() {
    const value = this.state.value;
    return <h1>{value}</h1>;
  }
}

class App extends React.Component {
    constructor(props) {
        super(props);
        this.userNameRef = React.createRef();
    }

  clickHandler() {
    var name = document.getElementById('name_input').value;
    this.userNameRef.current.setState({value: name});
  }

  render() {
    return (
      <div>
        <button onClick={this.clickHandler.bind(this)}>Change Username</button>
        <input id="name_input" type="text" />
        <Username ref={this.userNameRef} />
      </div>
    );
  }
}

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

Make sure you understand the code and don't be like me, forgetting the bind method :-)

Upvotes: 0

logos_164
logos_164

Reputation: 786

This component structure probably isn't your best bet. Typically you want to have a Class component at the top with functional components on the bottom, and call those functional components within the Class component.

So rendering <button> within App is just making things hard for you. In App you should just be rendering <Username /> and have <Username /> holding your logic:

class Username extends Component {
constructor(props){
this.state = { usernameValue: ''};
this.onInputChange = this.onInputChange.bind(this);
this.changeUsername = this.changeUsername.bind(this);
}

onInputChange(event) {
this.setState({usernameValue: event.target.value});
}

changeUsername() {
//Update username in the DB
db.record = this.state.usernameValue
}

render(){

return (
<div>//Wrapper div
<input onChange={this.onInputChange} value={this.state.usernameValue} />
<button onClick={this.changeUsername}>Change Username</button>
</div>
);

}

}

function App(){
return(
<Username />
);
}

I did this a different way than what you were trying as you were trying to update the username by clicking the button, which you can do, but it would be better to update the state as you input the username, then use the button click as a form submission or something along those lines.

A good resource for this is here

Upvotes: -1

Tobias Lins
Tobias Lins

Reputation: 2651

Just found it out. Seems like you need to use refs for everything. No state or anything allowed! Please note that you should not do it like that in a real world app :)

function App() {
  const ref = React.useRef()
  const inputRef = React.useRef()

  function clickHandler() {
    ref.current.changeValue(inputRef.current.value)
  }


  return (
    <div>
      <button onClick={clickHandler}>Change Username</button>
      <input type="text" ref={inputRef}  />
      <Username ref={ref} />
    </div>
  );
}

Upvotes: 6

Related Questions