Why is my React form not updating dynamically? - javascript

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} />
);
};

Related

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?
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

Cannot update the app state from custom component using React and Redux

I have the following Codesandbox.io:
https://codesandbox.io/s/qxkq5vvm1q
which is a basic ReactJS / Redux application.
The key components here are:
a Select which gets its values something like through this way: Redux (state manager) -> PanelMaterialSize (container) -> Select
one Updater component which takes care of update the values available on the Select through Redux
Alert button, which when clicked should alert the value stored on the store
What should happen is:
when the user changes an option on the Select, that value should be stored on the store. This is actually happening properly - OK
if the Select gets its values changed (for example because the Updater component), then it should automatically change the value stored on the store with the value it is showing (something similar as if the user changes the value on it). Unfortunately this is not happening - The Goal
Here are some of the codes:
./src/controls/Select/Select.js
import React, { Component } from "react";
import "./Select.scss";
class Select extends Component {
constructor(props) {
super(props);
let { name, data, className, ...controlProps } = this.props;
this.name = name;
this.data = data;
this.controlProps = controlProps;
this.state = {
[name]: data,
className
};
}
render() {
let data = this.state[this.name];
return (
<div className="control-select" {...this.controlProps}>
<div className="custom-dropdown custom-dropdown--grey">
<select className="custom-dropdown__select custom-dropdown__select--grey">
{this.props.data.length > 0 &&
this.props.data.map((elem, index) => {
return (
<option value={elem.value} key={index}>
{elem.text}
</option>
);
})}
</select>
</div>
</div>
);
}
}
export default Select;
src/controls/PanelMaterialSize/PanelMaterialSize.js
import React, { Component } from "react";
import { connect } from "react-redux";
import "./PanelMaterialSize.scss";
import Select from "../Select/Select";
import { setThemeList, setSelectedTheme } from "../../store/AppConfig/actions";
class PanelMaterialSize extends Component {
constructor(props) {
super(props);
this.state = {
selection: "",
options: []
};
}
handleChange = e => {
let target = e.target;
let value = target.value;
this.props.setSelectedTheme(value);
};
render() {
return (
<div className="partial-designer-panel-material-size">
<div>
<div className="label-input">
<div className="label">THEME</div>
<div className="input">
<Select
name="selection"
value={this.state.selection}
data={this.props.themeList}
style={{ width: "100%" }}
onChange={this.handleChange}
/>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = appState => {
return {
themeList: appState.appConfig.themeList,
selectedTheme: appState.appConfig.selectedTheme,
};
};
const mapDispatchToProps = dispatch => {
return {
setThemeList: themeList => dispatch(setThemeList(themeList)),
setSelectedTheme: selectedTheme => dispatch(setSelectedTheme(selectedTheme)),
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(PanelMaterialSize);
Any idea on how to make the point 2 work?
If possible, please, provide back your solution on a forked Codesandbox.io.
Thanks!
Updater component is producing new list of themes every 3seconds
It must also dispatch setSelectedTheme action to update selected theme in application state

react-select can load async data

I'm trying to build a select component using react-select plugin.
In the process of implementing this project, I have some kind of tricky problem with that. Check out my source code here: https://codesandbox.io/s/j148r99695
The problem that I have is I want to fetch all genresList data from the server and mapping them to select component. But somehow or I do wrong something, It's not working. Please see source code above to help me.
I fetch data from Movies component. Its work well and I pass a props to FormFilter component: <FormFilter genresList={this.state.genres} />. And in the FormFilter component, I check this.props.genresList, it's available. But when I'm trying to assign it to FormFilter state and console.log("state", this.state.genres); that. It's empty. Anyone can tell me why?
Default react-select using value and label to display data to select component. But you know some cases we have to custom that. I try it out by using map to transform to other arrays. But It's the best way? How can I custom valueKey and labelKey.
I'm using react-select beta version2.
UPDATE: I was fixed my project. Please check out the link below. Somehow it's not working. I was commend inside source code.
https://codesandbox.io/s/moym59w39p
So to make it works I have changed the FormFilter.js implementation:
import React, { Component } from "react";
import * as Animated from "react-select/lib/animated";
import AsyncSelect from "react-select/lib/Async";
class FormFilter extends Component {
constructor(props) {
super(props);
this.state = {
inputValue: "",
selectedOption: "",
genres: []
};
}
selectGenreHandleChange = newValue => {
const inputValue = newValue.replace(/\W/g, "");
this.setState({ inputValue });
console.log(inputValue);
};
componentDidMount() {
this.genresOption();
}
filterGenres = inputValue => {
const genres = this.genresOption();
//HERE - return the filter
return genres.filter(genre =>
genre.label.toLowerCase().includes(inputValue.toLowerCase())
);
};
promiseOptions = inputValue => {
return new Promise(resolve => { // HERE - you have to return the promise
setTimeout(() => {
resolve(this.filterGenres(inputValue));
}, 1000);
});
};
genresOption() {
const options = [];
const genres = this.props.genresList.genres; //HERE - array is genres in genresList
if (genres && genres instanceof Array) {
genres.map(genre => options.push({ value: genre.id, label: genre.name}));
}
return options;
}
render() {
const { inputValue } = this.state;
if (this.state.genres) console.log("state", this.state.genres);
if (this.props.genresList)
console.log("Movies props", this.props.genresList);
return (
<div className="filter_form">
<span className="search_element full">
<label htmlFor="genres">Genres</label>
<AsyncSelect
className="select genres"
classNamePrefix="tmdb_select"
isMulti
isSearchable="true"
isClearable="true"
cacheOptions
components={Animated}
value={inputValue}
defaultOptions
onInputChange={this.selectGenreHandleChange}
loadOptions={this.promiseOptions}
/>
</span>
</div>
);
}
}
export default FormFilter;
I have write a comment "HERE - something" to let you know what I changed. There are not big problems :)
I did some changed in your FIDDLE and it's works for me
Something like
import React, {Component} from "react";
import { render } from 'react-dom';
import Movies from './Movies';
import "./styles.css";
class App extends Component {
render() {
return (
<div className="App">
<Movies />
</div>
);
}
}
let a = document.getElementById("root");
render(<App />, a);

Why changing parent state via button in react is not possible with material-ui button?

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?

React - Can A Child Component Send Value Back To Parent Form

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

Categories

Resources