Getting text inputs to work in ReactJS - javascript

I have a few text inputs on my form that are not working correctly. I cannot see what is being typed and the fields unfocus after just one character.
Here's the pieces of my code where it's at:
constructor(props) {
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
};
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
this.removeItem = this.removeItem.bind(this)
}
componentDidMount() {
// componentDidMount() is a React lifecycle method
this.addQuestion();
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
//yada yada
render() {
//yada yada
{this.state.questions.map((question, index) => {
return (
<li>
<div key={uuid()}>
Question
{question}<br />
<button onClick={ () => this.removeItem(index) }>
Remove Question
</button>
Answer Choices<br />
{Array.from({ length: 4 }, () => (
<div>
<input type="checkbox" key={uuid()}/>
<input type="text" key={uuid()} onChange={this.handleChange} />
</div>
))}
</div>
</li>
);
})}
//yada yada
}
export default App;
I am very new to this so any tips directly related to this issue or not would be super helpful. Thanks!

You are giving your elements unique keys each render with uuid(), which will destroy the element and mount a new one. Remove the key={uuid()} and it will work.
{this.state.questions.map((question, index) => {
return (
<li key={index}>
<div>
Question
{question}
<br />
<button onClick={() => this.removeItem(index)}>
Remove Question
</button>
Answer Choices<br />
{Array.from({ length: 4 }, (_element, index) => (
<div key={index}>
<input type="checkbox" />
<input type="text" onChange={this.handleChange} />
</div>
))}
</div>
</li>
);
})}

Related

Setting values from radio buttons generated with map in react js

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

React Trying to edit input but got an error

I'm working on the edit/update the prices in the price list in my React app. I'm almost close but was unable to input any changes. Every time I tried to change the price in the input, I get an error saying "TypeError: onChange is not a function"...
I'm trying to write my code that's almost similar to this tutorial: https://medium.com/the-andela-way/handling-user-input-in-react-crud-1396e51a70bf#8858
So far, I was able to toggle the price between input field and back but I'm trying to edit the input to save any changes...What am I missing? I kept checking between my code and this tutorial to make sure everything is working...
Here's my code (functions) in the Parent component, PriceForm.js:
toggleItemEditing = index => {
this.setState({
priceArr: this.state.priceArr.map((item, itemIndex) => {
if (itemIndex === index) {
return {
...item,
isEditing: !item.isEditing
}
}
return item;
})
});
};
handlePriceUpdate = (event, index) => {
const target = event.target;
const value = target.value;
const number = target.number;
this.setState({
priceArr: this.state.priceArr.map((item, itemIndex) => {
if (itemIndex === index) {
return {
...item,
[number]: value
}
}
return item;
})
});
console.log("price update", event);
};
and where it's called in:
{this.state.priceArr.map((props, index) => (
<PriceBox
{...props}
key={props.date}
toggleEditing={this.toggleItemEditing}
handleDeletePrice={this.handleDeletePrice}
onChange={this.handlePriceUpdate}
/>
))}
And here's my code for the Child component, SinglePriceBox.js:
import React, { Component } from "react";
export default class SinglePricebox extends Component {
constructor(props) {
super(props);
this.state = {
isInEditMode: false,
todaydate: this.props.date
};
this.toggleEditPriceSubmission = this.toggleEditPriceSubmission.bind(this);
}
toggleEditPriceSubmission() {
this.setState(state => ({ isInEditMode: !state.isInEditMode }));
}
render() {
const { isInEditMode, onChange, index } = this.state;
return (
<div className="pricebox">
<article className="pricetable">
<table>
<tbody>
<tr>
<td className="date-width">{this.props.date}</td>
<td className="price-width">
{isInEditMode ? (
<input type="text" name="number" value={this.props.number} onChange={event => onChange(event, index)} />
) : (
this.props.number
)}
</td>
<td className="editing-btn">
<button
type="button"
className="edit-btn"
onClick={this.toggleEditPriceSubmission}
>
{isInEditMode ? "Save" : "Edit"}
</button>
</td>
<td>
{this.props.handleDeletePrice && (
<button
type="button"
className="delete-btn"
onClick={() => this.props.handleDeletePrice(this.props.date)}
>
X
</button>
)}
</td>
</tr>
</tbody>
</table>
</article>
</div>
);
}
}
You can check out my demo at https://codesandbox.io/s/github/kikidesignnet/caissa. You will be able to check out the error if you click on Prices button, then click on Edit button to change the price in the input field that appears.
import React, { Component } from "react";
export default class SinglePricebox extends Component {
constructor(props) {
super(props);
this.state = {
isInEditMode: false,
todaydate: this.props.date
};
this.toggleEditPriceSubmission = this.toggleEditPriceSubmission.bind(this);
this.handleChange = this.handleChange.bind(this);
}
toggleEditPriceSubmission() {
this.setState(state => ({ isInEditMode: !state.isInEditMode }));
}
handleChange = (e, index) => {
// write your code here
}
render() {
const { isInEditMode, index } = this.state;
return (
<div className="pricebox">
<article className="pricetable">
<table>
<tbody>
<tr>
<td className="date-width">{this.props.date}</td>
<td className="price-width">
{isInEditMode ? (
<input type="text" name="number" value={this.props.number} onChange={event => this.handleChange(event, index)} />
) : (
this.props.number
)}
</td>
<td className="editing-btn">
<button
type="button"
className="edit-btn"
onClick={this.toggleEditPriceSubmission}
>
{isInEditMode ? "Save" : "Edit"}
</button>
</td>
<td>
{this.props.handleDeletePrice && (
<button
type="button"
className="delete-btn"
onClick={() => this.props.handleDeletePrice(this.props.date)}
>
X
</button>
)}
</td>
</tr>
</tbody>
</table>
</article>
</div>
);
}
}
In the following line:
<input type="text" name="number" value={this.props.number} onChange={event => onChange(event, index)} />
You're calling this.state.onChange but there is no onChange in your state:
this.state = {
isInEditMode: false,
todaydate: this.props.date
};
After looking at your codesandbox, it seems that onChange is passed as a props to PriceBox, so you should do this in SinglePriceBox render():
const { isInEditMode, index } = this.state;
const { onChange } = this.props;
This will remove the error you were having, but the update still doesn't work because target.number is undefined in PriceForm.handlePriceUpdate :(
However target.name is defined and equal to 'number' which is a valid key in your price list
the problem is in how you pass your function as a prop. You should pass the function call in this way:
<PriceBox
{...props}
[...]
onChange={(e) => this.handlePriceUpdate(e, index)}
/>
Then call it in your child component:
<input
type="text"
[...]
onChange={event => this.props.onChange(event, index)}
/>
Also, I would not using index here, I would rather use an ID from your object instead

React JS: How to Create a preview page from the current view

I've created a React component that takes inputs from other components to display text of various size in a view. Since it is basically a form, what I want to do is pass the current view into another page where I will then post that view to my database as JSON.
Since the state of the input fields are not set in this component, I'm not sure how I would pass them as props to a new view.
This is a condensed version of what my data input component looks like:
INPUTSHOW.JSX
export default class InputShow extends Component {
componentDidMount() {
autosize(this.textarea);
}
render() {
const { node } = this.props;
...
return (
<div className="editor-div" >
{
(node.type === 'buttonA') ?
<textarea
style={hlArea}
ref={a => (this.textarea = a)}
placeholder="type some text"
rows={1}
defaultValue=""
id={node.id} className='editor-input-hl' type="text" onChange={this.props.inputContentHandler} />
:
(node.type === 'buttonB')
?
<textarea
style={subArea}
ref={b => (this.textarea = b)}
placeholder="type some text"
rows={1}
defaultValue=""
id={node.id} className='editor-input-sub' type="text" onChange={this.props.inputContentHandler} />
:
""
}
</div >
)
}
}
This works fine in creating inputs in a current view. I then pass those values to TextAreaField.JSX
export default (props) => {
return (
<>
<button><Link to={{
pathname: '/edit/preview',
text: props.inputsArray
}}>preview</Link></button>
<div className='view'>
{
props.inputsArray.map(
(node, key) => <InputShow key={key} node={node} inputContentHandler={props.inputContentHandler} />
)
}
</div>
</>
)
}
and then finally that is rendered in my Edit.JSX form:
export default class Edit extends React.Component {
constructor(props) {
super(props)
UniqueID.enableUniqueIds(this);
this.state = {
inputs: [],
text: ''
}
}
...
createPage = async () => {
await this.props.postPage(this.state.text)
}
// Handler for listen from button.
buttonCheck = (e) => {
index++;
const node = {
id: this.nextUniqueId() + index,
type: e.target.id,
text: '',
image: true
}
this.setState(
prev => ({
inputs: [...prev.inputs, node]
})
)
console.log(this.state.inputs);
}
inputContentHandler = (e) => {
let newArray = this.state.inputs;
let newNode = newArray.find((node) => {
return (node.id === e.target.id)
})
newNode.text = e.target.value;
this.setState({ inputs: newArray });
console.log(this.state.inputs);
}
render() {
return (
<div>
<InnerHeader />
<div className='some-page-wrapper'>
<div className='row'>
<div className="dash-card-sm">
<br />
<EditButtonContainer buttonCheck={this.buttonCheck} />
<Route path='/edit/form' render={() => (
<TextAreaField
inputsArray={this.state.inputs}
inputContentHandler={this.inputContentHandler}
/>
)}
/>
<Route path='/edit/preview' render={(props) => (
<Preview
inputs={this.state.inputs}
text={this.state.text}
createPage={this.createPage}
/>
)}
/>
<br /> <br />
{/* Button Header */}
</div>
</div>
</div>
</div>
)
}
}
The problem is that I don't know how I should be passing the rendered view to the Preview.jsxcomponent. I'm still new to react (4 months)...Any help in pointing me in the right direction would be appreciated.

How to add multiple inputs to an object for rendering in ReactJS

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>

Printing list in <ul>

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

Categories