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?
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>
);
}
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 :-)
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));
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
Related
I am building a simple react app and I am beginner. I am trying to send state as props to another component But when I access props like this.props.name then it is showing undefined.
App.js:
class Home extends React.Component {
state = {
fields : {
name : '',
}
}
updateField = evt => {
const fields = Object.assign({}, this.state.fields);
fields[evt.target.name] = evt.target.value;
console.log(evt.target.value)
this.setState({fields})
}
render() {
return(
<div>
<form>
<input
name="name"
id="name_id"
onChange={this.updateField}
value={this.state.fields.name}
/>
</form>
<Link to='/second_component'><SecondComponent name={this.state.fields} />Submit</Link>
<Routes>
<Route path='/second_component' element={<SecondComponent/>} />
</Routes>
</div>
)
}
}
class SecondComponent extends React.Component {
render() {
return (
<div style={{color: "white"}}>
{console.log(this.props.name)}
</div>
)
}
}
export default Home;
I have tried many times but it is still not working. When I try to console.log then it is showing "undefined".
Change fields[evt.target.name] = evt.target.value; to fields.name = evt.target.value; and name={this.state.fields} to name={this.state.fields.name}. Should work.
I am fairly new to software development. I am currently working on a project on Codecademy where the cat should copy what you say, unless it has the tape over it's mouth (pic attached). After many hours, I just can't seem to find why whatever I type into the input field, doesn't dynamically update in the p field. Any guidance would be appreciated!
This is also my first Stack Overflow post, so if I am missing anything, please let me know.
Both states of the cat - without tape, it should copy what is in the input field and vice versa
// CopyCat.js
import React from 'react';
import { styles } from '../styles';
const images = {
copycat: 'https://content.codecademy.com/courses/React/react_photo_copycat.png',
quietcat: 'https://content.codecademy.com/courses/React/react_photo_quietcat.png'
};
export class CopyCat extends React.Component {
render() {
const copying = this.props.copying;
const toggleTape = this.props.toggleTape;
const input = this.props.input;
const handleChange = this.props.handleChange;
return (
<div style={styles.divStyles}>
<h1 style={{ marginBottom: 80 }}>Copy Cat</h1>
<input
type='text'
value={this.input}
onChange={this.handleChange} />
<img
alt='cat'
src={copying ? images.copycat : images.quietcat}
onClick={toggleTape}
style={styles.imgStyles}
/>
<p>{this.copying && this.input}</p>
</div>
);
};
}
// CopyCatContainer.js
import React from 'react';
import ReactDOM from 'react-dom';
import { CopyCat } from '../components/CopyCat';
const images = {
copycat: 'https://content.codecademy.com/courses/React/react_photo_copycat.png',
quietcat: 'https://content.codecademy.com/courses/React/react_photo_quietcat.png'
};
class CopyCatContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
copying: true,
input: ''
};
this.toggleTape = this.toggleTape.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({input: e.target.value})
}
toggleTape() {
this.setState({copying: !this.state.copying})
}
render() {
const copying = this.state.copying;
const toggleTape = this.toggleTape
return (
<CopyCat
copying={this.state.copying}
toggleTape={this.toggleTape} />
);
};
}
ReactDOM.render(<CopyCatContainer />, document.getElementById('app'));
You are not passing the input and the handleChange method as props to the CopyCat component
You need to pass input to <CopyCat /> component like below;
return (
<CopyCat
copying={this.state.copying}
toggleTape={this.toggleTape}
input={this.state.input} />
);
you are not passing handlechange function to copycat component. Pass it and it should work fine. Hope this would help.
You need to pass both input as well as handleChange props to CopyCat component to reflect text change, like this:
render() {
const copying = this.state.copying;
const input = this.state.input;
return (
<CopyCat
copying={copying}
input={input}
toggleTape={this.toggleTape}
handleChange={this.handleChange} />
);
};
I'm new in react, I'm trying to make calculator using create-react-app starter. To do this, I want to update parent state, where I want to have string of numbers and operations(+, mod, -/ * etc.), so I can count everything later easily. I wanted to have pretty buttons, so I decided to use material-ui.
Here is my code
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
class Calc extends React.Component {
constructor(props) {
super(props)
this.state = {
equation: ''
}
this.handleChange = this.handleChange.bind(this)
this.one = this.one.bind(this)
}
handleChange (e) {
this.setState({
equation: e.target.value
})
}
one (e) {
console.log('dodano');
let newValue = this.state.equation.toString() + e.target.value.toString();
this.setState({
equation: newValue
})
}
render() {
return (
<MuiThemeProvider>
<div id='calc'>
Hello <br />
Change Name:
<input
type="text"
value={this.state.equation}
onChange={this.handleChange}
/>
<input type='button' id='myButton1' value={1} onClick={this.one}/>
<RaisedButton className = 'myButton2' primary={true} value = {2} label='2' onClick={this.one}/>
<MyButton className = 'myButton3' secondary={true} label= '3' value = {3} onClick= {this.one} />
</div>
</MuiThemeProvider>
)
}
}
class App extends Component {
render() {
return (
<Calc/>
);
}
}
class MyButton extends React.Component {
render() {
return(
<RaisedButton className = {this.props.className} secondary = {true} value = {this.props.value} label= {this.props.label}/>
)
}
}
export default App;
Each button should insert value to the input label, instead I got error with second button and nothing in third button(function is not invoked?)
Only first button is working. Any idea why I can't change state of parent like with first button, but with material-ui components?
I am building a movie search React app using themoviedb.org API. in order to an make ajax call to pull the list of movies I need to get input value as a variable and feed to the url, but not sure how to fetch a value that belongs to another component.
I've made an extensive online search, but they mainly refer to the case when it happens inside the same component, and using ref is discouraged.
So what would be the best way (or at least most common or the simplest way) to fetch the input value variable from one component to pass down to another and attach to the end of url, while:
1) Keeping global space clean
2) Organizing the entire app in 'React way'
3) Keeping components decoupled
?
Would React Router necessary in this case?
import React from 'react';
import './App.css';
import axios from 'axios';
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
console.log("state value is " + this.state.value);
var searchValue = this.movieName.value;
console.log("ref value is "+ searchValue)
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input className="movieName" type="text" ref={(input) => { this.movieName = input; }} value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
<h1>{this.state.value}</h1>
</form>
);
}
}
class App extends NameForm{ /* I am extending NameForm to get access to input value, but probably coupling components too tight */
constructor(props) {
super(props);
this.state ={
movie:[]
};
}
componentDidMount() {
let searchInput = "land"; /* This should be from SearchForm's input value */
let sortByPop = "&sort_by=popularity.desc";
let requestUrl = 'https://api.themoviedb.org/3/search/movie?api_key=f8c4016803faf5e7f424abe98a04b8d9&query=' + searchInput + sortByPop;
axios.get(requestUrl).then(response => {
this.setState({movie: response.data.results})
});
}
render() {
let baseImgURL = "https://image.tmdb.org/t/p/w185_and_h278_bestv2";
let posterImgPath = this.state.movie.map(movie => movie.poster_path);
let posterLink = baseImgURL + posterImgPath;
return(
<div className="App">
<Header />
<SearchForm />
<div>
{this.state.movie.map(movie =>
<div className="movieTitle">
<div className="movieCard">
<img className="posterImg" src= {`https://image.tmdb.org/t/p/w185_and_h278_bestv2/${movie.poster_path}`} alt={movie.title} />
<div className="searchFilmTitles" key={movie.id}>{movie.title}</div>
</div>
</div>
)}
</div>
</div>
)
}
}
export default App;
componentDidMount get called only once when your component get attached to the page. So it's not the correct place to call you search API. Instead, you should call it every time when the user clicks 'submit' button. For that, you need to bubble handleSubmit trigger to App component by passing a callback method as a prop to SearchForm component. Also, you don't need to use ref as you already have search text in your state.
SearchForm
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
event.preventDefault();
if(this.props.onSubmit && typeof this.props.onSubmit === "function"){
this.props.onSubmit(this.state.value);
}
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input className="movieName" type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
<h1>{this.state.value}</h1>
</form>
);
}
}
App
class App extends React.Component { /* I'm not sure why you extends NameForm and what NameForm does */
constructor(props) {
super(props);
this.state = {
movie:[]
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(value) {
let searchInput = value // you get the value of movieName input here
let sortByPop = "&sort_by=popularity.desc";
let requestUrl = 'https://api.themoviedb.org/3/search/movie?api_key=f8c4016803faf5e7f424abe98a04b8d9&query=' + searchInput + sortByPop;
axios.get(requestUrl).then(response => {
this.setState({movie: response.data.results})
});
}
render() {
let baseImgURL = "https://image.tmdb.org/t/p/w185_and_h278_bestv2";
let posterImgPath = this.state.movie.map(movie => movie.poster_path);
let posterLink = baseImgURL + posterImgPath;
// I'm not sure why use need above code as you don't use it anywhere
return(
<div className="App">
<Header />
<SearchForm onSubmit={this.handleSubmit}/>
<div>
{this.state.movie.map(movie =>
<div className="movieTitle">
<div className="movieCard">
<img className="posterImg" src= {`https://image.tmdb.org/t/p/w185_and_h278_bestv2/${movie.poster_path}`} alt={movie.title} />
<div className="searchFilmTitles" key={movie.id}>{movie.title}</div>
</div>
</div>
)}
</div>
</div>
);
}
}
The InputField & Button are custom components that go into a form to create a form. My issue is how do I send the data back up to form so that on button click, I can fire ajax on the form with data (username & password):
export default auth.authApi(
class SignUpViaEmail extends Component{
constructor(props){
super(props);
this.state = {
email : "",
password : ""
};
this.storeEmail = this.storeEmail.bind( this );
this.storePassword = this.storePassword.bind( this );
}
storeEmail(e){
this.setState({ email : e.target.value });
}
storePassword(e){
this.setState({ password : e.target.value });
}
handleSignUp(){
this.props.handleSignUp(this.state);
}
render(){
return(
<div className="pageContainer">
<form action="" method="post">
<InputField labelClass = "label"
labelText = "Username"
inputId = "signUp_username"
inputType = "email"
inputPlaceholder = "registered email"
inputClass = "input" />
<Button btnClass = "btnClass"
btnLabel = "Submit"
onClickEvent = { handleSignUp } />
</form>
</div>
);
}
}
);
Or Is it not recommended & I should not create custom child components within the form?
child component => InputField
import React,
{ Component } from "react";
export class InputField extends Component{
constructor( props ){
super( props );
this.state = {
value : ""
};
this.onUserInput = this.onUserInput.bind( this );
}
onUserInput( e ){
this.setState({ value : e.target.value });
this.props.storeInParentState({[ this.props.inputType ] : e.target.value });
}
render(){
return <div className = "">
<label htmlFor = {this.props.inputId}
className = {this.props.labelClass}>
{this.props.labelText}
</label>
<input id = {this.props.inputId}
type = {this.props.inputType}
onChange = {this.onUserInput} />
<span className = {this.props.validationClass}>
{ this.props.validationNotice }
</span>
</div>;
}
}
Error : I get the error e.target is undefined on the parent storeEmail func.
React's one-way data-binding model means that child components cannot send back values to parent components unless explicitly allowed to do so. The React way of doing this is to pass down a callback to the child component (see Facebook's "Forms" guide).
class Parent extends Component {
constructor() {
this.state = {
value: ''
};
}
//...
handleChangeValue = event => this.setState({value: event.target.value});
//...
render() {
return (
<Child
value={this.state.value}
onChangeValue={this.handleChangeValue}
/>
);
}
}
class Child extends Component {
//...
render() {
return (
<input
type="text"
value={this.props.value}
onChange={this.props.onChangeValue}
/>
);
}
}
Take note that the parent component handles the state, while the child component only handles displaying. Facebook's "Lifting State Up" guide is a good resource for learning how to do this.
This way, all data lives within the parent component (in state), and child components are only given a way to update that data (callbacks passed down as props). Now your problem is resolved: your parent component has access to all the data it needs (since the data is stored in state), but your child components are in charge of binding the data to their own individual elements, such as <input> tags.
Addendum
In response to this comment:
What if we render a list of the child component? Using this single source of truth in Lifting state up technique will let the parent controls all the state of all the child inputs right? So how can we access each of the value input in the child component to (which is rendered as list) from the parent component?
For this case, you may map a child component for each element in the list. For example:
class Parent extends Component {
//...
handleChangeListValue = index => event => {
this.setState({
list: this.state.list
.map((element, i) => i === index ? event.target.value : element)
});
}
//...
render() {
return this.state.list.map((element, i) => (
<Child
value={element}
onChangeValue={this.handleChangeListValue(i)}
/>
));
P.S. Disclaimer: above code examples are only for illustrative purposes of the concept in question (Lifting State Up), and reflect the state of React code at the time of answering. Other questions about the code such as immutable vs mutable array updates, static vs dynamically generated functions, stateful vs pure components, and class-based vs hooks-based stateful components are better off asked as a separate question altogether.
React class component
Parent.js
import React, { Component } from 'react';
import Child from './child'
class Parent extends Component {
state = {
value: ''
}
onChangeValueHandler = (val) => {
this.setState({ value: val.target.value })
}
render() {
const { value } = this.state;
return (
<div>
<p> the value is : {value} </p>
<Child value={value} onChangeValue={this.onChangeValueHandler} />
</div>
);
}
}
export default Parent;
Child.js
import React, { Component } from 'react';
class Child extends Component {
render() {
const { value , onChangeValue } = this.props;
return (
<div>
<input type="text" value={value} onChange={onChangeValue}/>
</div>
);
}
}
export default Child;
React hooks
Parent.js
import { useState } from "react";
import Child from "./child";
export default function Parent() {
const [value, changeValue] = useState("");
return (
<div>
<h1>{value}</h1>
<Child inputValue={value} onInputValueChange={changeValue} />
</div>
);
}
Child.js
export default function Child(props) {
return (
<div>
<input
type="text"
value={props.inputValue}
onChange={(e) => props.onInputValueChange(e.target.value)}/>
</div>
);
}
Parent.js
import SearchBar from "./components/SearchBar";
function App() {
const handleSubmit = (term) => {
//Log user input
console.log(term);
};
return (
<div>
<SearchBar onPressingEnter={handleSubmit} />
</div>
);
}
export default App;
Child.js
import { useState } from "react";
function SearchBar({ onPressingEnter }) {
const [UserSearch, setname] = useState("[]");
/* The handleChange() function to set a new state for input */
const handleChange = (e) => {
setname(e.target.value);
};
const onHandleSubmit = (event) => {
//prevent form from making a http request
event.preventDefault();
onPressingEnter(UserSearch);
};
return (
<div>
<form onSubmit={onHandleSubmit}>
<input
type="search"
id="mySearch"
value={UserSearch}
onChange={handleChange}
name="q"
placeholder="Search the siteā¦"
required
/>
</form>
</div>
);
}
export default SearchBar;
You can add a "ref name" in your InputField so you can call some function from it, like:
<InputField
ref="userInput"
labelClass = "label"
labelText = "Username"
inputId = "signUp_username"
inputType = "email"
inputPlaceholder = "registered email"
inputClass = "input" />
So you can access it using refs:
this.refs.userInput.getUsernamePassword();
Where getUsernamePassword function would be inside the InputField component, and with the return you can set the state and call your props.handleSignUp