I want to print a new <ul> list of <li> movies.
I don't see any list nor elements.
I also get a warning:
index.js:2178 Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://reactjs.org/docs/forms.html#controlled-components
in input (at index.js:54)
in label (at index.js:52)
in form (at index.js:51)
in div (at index.js:50)
in Movie (at index.js:70)
This is my code:
class Movie extends React.Component {
constructor(props) {
super(props);
this.state = {value: '',
list: [],
checked: true
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.addMovie = this.addMovie.bind(this);
this.listMovies = this.listMovies.bind(this);
}
handleChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit(event) {
event.preventDefault();
this.addMovie();
}
addMovie(value){
this.setState({ list: [...this.state.list, value] });
console.log(...this.state.list);
}
listMovies(){
return(
<ul>
{this.state.list.map((item) => <li key={this.state.value}>{this.state.value}</li>)}
</ul>
);
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Movie name:
<input name="movieName" type="text" value={this.state.movieName} onChange={this.handleChange} />
Favorite?
<input name="favorite" type="checkbox" checked={this.state.favorite} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<button onClick={this.listMovies}>
List Movies
</button>
</div>
);
}
}
ReactDOM.render(
<Movie />,
document.getElementById('root')
);
I would really want to print only my Favorites movies
I'm guessing you want a simple movies list with favorites. Not the best one but working code:
import React from 'react';
import { render } from 'react-dom';
class App extends React.Component {
state = {
favorite: false,
movieName: "",
movies: [],
filter: true,
};
handleChange = (event) =>
event.target.name === "favorite"
? this.setState({ [event.target.name]: event.target.checked })
: this.setState( { [ event.target.name]: event.target.value } );
handleSubmit = ( event ) => {
event.preventDefault();
this.setState({
movies: [...this.state.movies, {name: this.state.movieName, favorite: this.state.favorite }]
});
}
listFavoriteMovies = () => (
<ul>
{this.state.movies
.filter( movie => movie.favorite )
.map( movie => <li>{movie.name}</li>)}
</ul>
);
listAllMovies = () => (
<ul>
{this.state.movies
.map(movie => <li>{movie.name}</li>)}
</ul>
);
changeFilter = () =>
this.setState( prevState => ( {
filter: !prevState.filter,
}))
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
<label>
Movie name:
<input name="movieName" type="text" onChange={this.handleChange} />
Favorite?
<input name="favorite" type="checkbox" onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
<p>Showing only favorite movies.</p>
<ul>
{
this.state.filter
? this.listFavoriteMovies()
: this.listAllMovies()
}
</ul>
<button onClick={this.changeFilter}>Click toggle for all/favorites.</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
if you initially pass undefined or null as the value prop, the
component starts life as an "uncontrolled" component. Once you
interact with the component, we set a value and react changes it to a
"controlled" component, and issues the warning.
In your code initialise movieName in your state to get rid of warning.
For more information check here
Related
I have a simple To do app that takes user input through a prompt and adds it to a list. I want to change the prompt and replace it with a text input bar. I've been having trouble implenting onChange and I'm hitting a wall trying to figure this out. I really appreciate the help as a beginner trying to learn.
import React, { useState } from 'react';
let id = 0
const Todo = props => (
<li>
<input type="checkbox" checked={props.todo.checked} onChange={props.onToggle} />
<button onClick = {props.onDelete}> delete</button>
<span>{props.todo.text}</span>
</li>
)
class App extends React.Component {
constructor() {
super()
this.state = {
todos: [],
}
}
addTodo() {
const text = prompt("TODO TEXT PLEASE!")
this.setState({
todos: [...this.state.todos, {id: id++, text: text, checked: false},
]
})
}
removeTodo(id) {
this.setState({
todos: this.state.todos.filter(todo => todo.id !== id )
})
}
toggleTodo(id) {
this.setState({
todos: this.state.todos.map(todo => {
if (todo.id !== id) return todo
return {
id: todo.id,
text: todo.text,
checked: !todo.checked,
}
})
})
}
render() {
return (
<div>
<div> Todo Count: {this.state.todos.length}</div>
<div> Unchecked Count: {this.state.todos.filter(todo => !todo.checked).length} </div>
<div className="App">
<label> Task Name:</label>
<input type="text" id="task"
/* onChange={(e)=> {
setTaskName(e.target.value);*/
/>
<button onClick={() => this.addTodo()}> Add ToDo </button>
</div>
<ul>
{this.state.todos.map(todo => (
<Todo
onToggle={() => this.toggleTodo(todo.id)}
onDelete={() => this.removeTodo(todo.id)}
todo={todo}
/>
))}
</ul>
</div>
)
}
}
To resolve this issue you are having, you will need to also add a local state to store your input field data (you could get this with a Controlled or Uncontrolled approaches):
this.state = {
currentTodo: "",
todos: [],
}
And then in your input onChange event as you already did (you were using a hook based approach though), you could add a state update for your currentTodo value as you write. And also the current state value in the input tag (as Cesare observed):
<input type="text" id="task"
value={this.state.currentTodo}
onChange={(e)=> {
this.setState({ ...this.state, currentTodo: e.target.value});
}
/>
Finally, to obtain the wroten text you can get it in your addTodo method.
const text = this.state.currentTodo;
I am trying to :
generate radio buttons from a constant array using Map in react
let user select one and set the state with handleChange()
With the following code I was able to achieve 1, but for some reason when I try to display with handleChange() I see it is an empty string.
Could you please help me ?
Thanks
import React, { Component } from "react";
const members = ["Araki", "Ibata", "Fukutome", "Woods", "Alex", "Tatsunami"];
export default class MyRadio extends Component {
constructor(props) {
super(props);
this.state = {
lastName: "",
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
console.log("handleChange() e:" + e.target.value);
this.setState({
[e.target.name]: e.target.value,
});
}
render() {
console.log("render");
return (
<form>
<div>
{members.map((item) => (
<React.Fragment>
<label htmlFor={item.name}> {item}</label>
<input
name="lastName"
key={item.name}
id={item.name}
value={item.name}
type="radio"
onChange={this.handleChange}
/>
</React.Fragment>
))}
</div>
<div></div>
</form>
);
}
}
To make this workable solution you have to change the members as follow
const members = [{name: "Araki", name: "Ibata", ...}];
array should be a object array with each object has name property because in the map you are expecting name should be there as item.name.
Or either you have to change the loop without item.name you have to use item
{members.map((item) => (
<React.Fragment>
<label htmlFor={item}> {item}</label>
<input
name="lastName"
key={item}
id={item}
value={item}
type="radio"
onChange={this.handleChange}
/>
</React.Fragment>
))}
Hey guys i am trying to assign a fucntion on my chechbox select all button to flip the state when button is clicked but i am doing something wrong . Can somebody help me ?
My state :
constructor(props) {
super(props);
this.state = {
allCheckboxes: false
};
handleAllCheckboxes = (e) => {
const allCheckboxesChecked = e.target.checked
let checkboxes = document.getElementsByName('checkbox')
this.setState({
allCheckboxes: allCheckboxesChecked
})
console.log(allCheckboxesChecked)
My single checkbox :
<Checkbox
checked={this.handleAllCheckboxes ? true : false}
name='checkbox'
color='default'
value={JSON.stringify({ documentId: rowData.documentId, documentNumber: rowData.documentNumber })}
onClick={this.handleCheckboxClick}
/>
My select all checkbox:
<Checkbox
onChange={this.handleAllCheckboxes}
indeterminate
/>Select All
The problem is that no matter what i do the state stay the same . It doesnt flip to true or false .
UPDATE
UPDATE
https://codesandbox.io/s/upbeat-khorana-j8mr6
Hi Your Checkbox handler should lie out of constructor.
like below:
constructor(props) {
super(props);
this.state = {
allCheckboxes: true
};
}
handleAllCheckboxes = (e) => {
const allCheckboxesChecked = e.target.checked
let checkboxes = document.getElementsByName('checkbox')
this.setState({
allCheckboxes: allCheckboxesChecked
})
console.log(allCheckboxesChecked)
}
and you have written checked={this.handleAllCheckboxes ? true : false} which looks wrong.Because **this.handleAllCheckboxes is already defined and therefore it will always return true.( Because function is always available.) **. Secondly handleAllCheckboxes is also not returning any true/false.
You need to keep your checkboxes state in state, when clicking select all change their state to true and vise versa.
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
checkBoxes: {
vehicle1: false,
vehicle2: false,
vehicle3: false,
}
};
}
handleCheckBoxes = (checkBox, checkAll = false) => {
if (checkAll) {
const checkBoxes = { ...this.state.checkBoxes };
Object.keys(checkBoxes).forEach((key) => {
checkBoxes[key] = checkBox.target.checked;
});
this.setState({
checkBoxes: checkBoxes
})
return;
}
const { checked, name } = checkBox.target;
this.setState(
prevState => {
return {
checkBoxes: { ...prevState.checkBoxes, [name]: checked }
};
},
() => console.log(this.state)
);
// console.log(checkBox.target.checked);
};
render() {
return (
<div>
<label>
<input
type="checkbox"
onChange={e => this.handleCheckBoxes(e, true)}
name="vehicle1"
value="Bike"
/>
Select All
</label>
<br />
<input
type="checkbox"
onChange={this.handleCheckBoxes}
name="vehicle1"
value="Bike"
checked={this.state.checkBoxes["vehicle1"]}
/>
<input
type="checkbox"
onChange={this.handleCheckBoxes}
name="vehicle2"
value="Car"
checked={this.state.checkBoxes["vehicle2"]}
/>
<input
type="checkbox"
onChange={this.handleCheckBoxes}
name="vehicle3"
value="Boat"
checked={this.state.checkBoxes["vehicle3"]}
/>
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>
There is a package grouped-checkboxes which can solve this problem.
You can simply wrap the checkboxes in a CheckboxGroup and add an AllChecker.
import React from 'react';
import {
CheckboxGroup,
AllCheckerCheckbox,
Checkbox
} from "#createnl/grouped-checkboxes";
const App = (props) => {
const { products } = props
return (
<CheckboxGroup onChange={console.log}>
<label>
<AllCheckerCheckbox />
Select all
</label>
{options.map(option => (
<label>
<Checkbox id={option.id} />
{option.label}
</label>
))}
</CheckboxGroup>
)
}
More examples see https://codesandbox.io/s/grouped-checkboxes-v5sww
I'm using react-select to render two types of "job" options. From the dropdown the user is able to select either of the options. Based on the option selected they should render different divs that contain more input values. The input values are dependent on the selectedOption. I have two class components with render methods that should display the different inputs corresponding to the jobType
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedOption: null
};
this.onChange = this.onChange.bind(this);
this.handleChange = this.handleChange.bind(this);
}
onChange = e => {
this.set({ [e.target.name]: e.target.value });
console.log([e.target.value]);
};
handleChange = selectedOption => {
this.setState({ selectedOption });
console.log("Option selected: ", selectedOption);
};
render() {
const { selectedOption } = this.state;
return (
<div>
<fieldset>
<legend>Parameters</legend>
<label htmlFor="jobType" style={{ display: "block" }}>
jobType:
</label>
<div>
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
placeholder="Select a jobType..."
isSearchable={options}
/>
</div>
</fieldset>
<div>
{selectedOption === "Batch" ? (
<BatchParams />
) : selectedOption === "Streaming" ? (
<StreamingParams />
) : null}
</div>
</div>
);
}
}
So let's say when the Streaming option is selected from the dropdown the following class component should be rendered:
class StreamingParams extends React.Component {
constructor(props) {
super(props);
this.state = {
maxHits: 1
};
this.handleMaxHitsChange = this.handleMaxHitsChange.bind(this);
}
handleMaxHitsChange = e => {
this.set({ [e.target.name]: e.target.valu });
};
render() {
return (
<div>
<label>maxHits:</label>
<input
type="number"
name="maxHits"
onChange={this.handleMaxHitsChange}
placeholder="1"
min="1"
step="1"
required
/>
</div>
);
}
}
I can't quite get the additional input boxes to render, not sure if I'm taking the correct approach here. I have included a codesandbox with my issue.
So it looks like the value that is getting set in this.state.selectedOption is an object with a value and a label field.
So you need to key off of those. Instead of doing this
{selectedOption === "Batch" ? (
<BatchParams />
) : selectedOption === "Streaming" ? (
<StreamingParams />
) : null}
Do this
{selectedOption && selectedOption.value === "batch" ? (
<BatchParams />
) : selectedOption && selectedOption.value === "streaming" ? (
<StreamingParams />
You should opt for value instead of label in case of localization or translations.
Fork
https://codesandbox.io/s/ll5p30nx5q
Only the ternary operator has to be changed: find an updated codesandbox here.
In the ternary operators you will have to check this.state.selectedOption.value as that is where your option value is stored.
{!this.state.selectedOption ?
null :
this.state.selectedOption.value === "batch" ? (
<BatchParams />
) : (
<StreamingParams />
)
}
Also you will have to first check for null as that is the initial value you get for this.state.selectedOption when no element in the select has been set.
The issue was that you have to take the value in the selectedOptions and assign that value to the selectedOption in the event handler setState. You were trying to assign an object and therefore the issue. Have made the changes in the sandbox also. Hope it helps.
import React from "react";
import ReactDOM from "react-dom";
import Select from "react-select";
import "./styles.css";
const options = [
{ value: "batch", label: "Batch" },
{ value: "streaming", label: "Streaming" }
];
class BatchParams extends React.Component {
constructor(props) {
super(props);
this.state = {
batchNumber: 1
};
this.handleBatchNumChange = this.handleBatchNumChange.bind(this);
}
handleBatchNumChange = e => {
this.set({ [e.target.name]: e.target.valu });
};
render() {
return (
<div>
<label>batchNumberSize:</label>
<input
type="number"
name="batchNumberSize"
onChange={this.handleBatchNumChange}
placeholder="1"
min="1"
step="1"
required
/>
</div>
);
}
}
class StreamingParams extends React.Component {
constructor(props) {
super(props);
this.state = {
maxHits: 1
};
this.handleMaxHitsChange = this.handleMaxHitsChange.bind(this);
}
handleMaxHitsChange = e => {
this.set({ [e.target.name]: e.target.valu });
};
render() {
return (
<div>
<label>maxHits:</label>
<input
type="number"
name="maxHits"
onChange={this.handleMaxHitsChange}
placeholder="1"
min="1"
step="1"
required
/>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedOption: null
};
this.onChange = this.onChange.bind(this);
this.handleChange = this.handleChange.bind(this);
}
onChange = e => {
this.set({ [e.target.name]: e.target.value });
console.log([e.target.value]);
};
handleChange =( {value}) => {
console.log(value);
this.setState({ selectedOption: value });
};
render() {
const { selectedOption } = this.state;
return (
<div>
<fieldset>
<legend>Parameters</legend>
<label htmlFor="querySchemaName" style={{ display: "block" }}>
jobType:
</label>
<div>
<Select
value={selectedOption}
onChange={this.handleChange}
options={options}
placeholder="Select a jobType..."
isSearchable={options}
/>
</div>
</fieldset>
<div>
{selectedOption === "batch" && <BatchParams /> }
{selectedOption === "streaming" && <StreamingParams />}
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
New to React and can manage rendering single input values, but I'm trying to render the value of multiple inputs in a list form and can't get my head around it.
I'm trying to add the inputs into an object, and push the object into an array (this is the main issue i'm having), and then render the items in the array. I've used jobs as an example showing what I've managed so far... Can anyone guide me in the right direction?
class App extends Component {
constructor(props){
super(props);
this.state = {
post: {
name: '',
description: ''
},
jobs: []
}
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
};
handleChange(e){
this.setState({
post.name: e.target.value,
post.name: e.target.value
})
}
handleSubmit(e){
e.preventDefault();
// This is where i've trying to push the inputs into an object...
this.setState({
post.name: '',
post.description: '',
jobs: this.state.items.concat(this.state.post)
})
}
render() {
const listItems = this.state.jobs.map((data, key) =>
<li key={key}>{data}</li>
)
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<form>
<input onChange={this.handleChange} type="text" value={this.state.post.name} placeholder="post name" />
<input onChange={this.handleChange} type="text" value={this.state.post.description} placeholder="post description" />
<button onClick={this.handleSubmit}>Submit</button>
</form>
<ul>
{listItems}
</ul>
</div>
);
}
}
Any help will be massively appreciated!
You need to render each field of an element in the jobs array separately because React can't render objects.
{ post.name: '', post.description: ''} is invalid JavaScript syntax. You must set post to a new object instead.
You can also use the name of the input to choose what field in the post object to update, so that you can share the same onChange handler for both inputs.
Example
class App extends React.Component {
state = {
post: {
name: "",
description: ""
},
jobs: []
};
handleChange = e => {
const { name, value } = e.target;
this.setState(prevState => ({
post: { ...prevState.post, [name]: value }
}));
};
handleSubmit = e => {
e.preventDefault();
this.setState(prevState => ({
jobs: [...prevState.jobs, prevState.post],
post: { name: "", description: "" }
}));
};
render() {
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Welcome to React</h1>
</header>
<form>
<input
name="name"
onChange={this.handleChange}
type="text"
value={this.state.post.name}
placeholder="post name"
/>
<input
name="description"
onChange={this.handleChange}
type="text"
value={this.state.post.description}
placeholder="post description"
/>
<button onClick={this.handleSubmit}>Submit</button>
</form>
<ul>
{this.state.jobs.map((job, index) => (
<li key={index}>
{job.name}: {job.description}
</li>
))}
</ul>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>