Reactjs - Submitting multiple lines from one textarea - arrays

I'm trying to allow a textarea to take a list of names and display the submission of names on the page. However, I want the user to only be able to submit one name per line.
How would one go about this? More specifically, how would one grab the line breaks and split them into, say... an array to be mapped through and displayed in a dom element.
In my code the state of names is an empty string, but I think it would be easier/more manageable if it were an array.
Thanks!
class Content extends Component {
constructor(props) {
super(props);
this.state = {
names: ""
};
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit = (e) => {
e.preventDefault();
}
handleChange = (e) => {
this.setState({
names: e.target.value
});
}
render() {
return (
<section className="flex">
<div className="content flex">
<div className="sandbox flex">
<div className="directions">
<h1>Please enter a list of names.</h1>
<h3>Select a langauge at the top of the page.</h3>
</div>
<form id="nameForm" className="names flex">
<div className="form-group">
<textarea
id="names"
name="hard"
value={this.state.names}
cols={20}
rows={20}
onChange={this.handleChange}
wrap="hard">
</textarea>
</div>
</form>
<button id="formButton" form="nameForm" type="submit">Submit</button>
<div className="nametags flex">
<div className="nametags-group flex">
<button type="button" className="nametag">{this.state.names}</button>
<p className="greeting">Hello there, happy to see you back!</p>
</div>
<div className="nametags-group flex">
<button type="button" className="nametag">John Doe</button>
<p className="greeting">Hello there, happy to see you back!</p>
</div>
</div>
</div>
</div>
</section>
)
}
}
export default Content;

This code should work perfectly for you:
import React, { Component } from 'react'
class Content extends Component {
constructor(props) {
super(props)
this.state = {
names: ''
}
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
handleSubmit = name => {
alert(`Submitted name: ${name}`)
}
handleChange = e => {
this.setState({
names: e.target.value
})
}
render() {
return (
<section className="flex">
<div className="content flex">
<div className="sandbox flex">
<div className="directions">
<h1>Please enter a list of names.</h1>
<h3>Select a langauge at the top of the page.</h3>
</div>
<form id="nameForm" className="names flex">
<div className="form-group">
<textarea
id="names"
name="hard"
value={this.state.names}
cols={20}
rows={20}
onChange={this.handleChange}
wrap="hard"
/>
</div>
</form>
<button id="formButton" form="nameForm" type="submit">
Submit
</button>
<div className="nametags flex">
<div className="nametags-group flex">
{this.state.names
.split('\n')
.filter(n => n) // to filter out empty names
.map((name, index) => (
<button
key={index}
type="button"
className="nametag"
onClick={() => this.handleSubmit(name)}
>
{name}
</button>
))}
<p className="greeting">Hello there, happy to see you back!</p>
</div>
</div>
</div>
</div>
</section>
)
}
}
export default Content
I think it's easier to keep the name state as string.
When rendering the names, split the string at \n, filter out the empty names (basically those extra newlines without names), and render the rest.
When clicking on those name buttons, call handleSubmit and pass name as argument.
So the rest to do is what you want to do with those names in handleSubmit.

Something like this:
handleChange = e => {
this.setState({
names: e.target.value,
submitNames: e.target.value.split(/\r?\n/)
});
};
And change how you display buttons:
{ this.state.submitNames.map(
(name) =>
<button
onClick={() => this.handleSubmit(name)}
type="button"
className="nametag"
>
{name}
</button>
)}

Related

Instead of storing values to local storage want to store values in Redux how can i Do?

Hello I am using class component in react JS.Instead of storing values to local storage store values in Redux how can i Do?How can we implement this in react JS. Below I am sharing my code.Please check my where I am doing wrong?
import React, {Component} from 'react';
import Header from '../Header';
import {connect} from 'react-redux';
class Step5 extends Component {
constructor(){
super()
this.state = {estimated:'1-2'}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
const { estimated, value } = event.target;
console.log('JJF',event.target.value)
this.setState({
selectedOption: event.target.value
});
};
handleSubmit = (e) => {
e.preventDefault();
var step5 = this.state.estimated;
console.log('estimated', JSON.stringify(step5))
console.log('step5',step5);
localStorage.setItem('step5', step5);
const data = {
id: new Date(),
step5:step5
}
this.props.dispatch({type:'ADD_STEP5',data});
window.open("/Step6" , "_self");
}
render(){
const {value} =this.state;
return(
<div>
<Header />
<section className="planing_outer">
<form onSubmit={this.handleSubmit}>
<h1>{value} fff</h1>
<div className="container">
<div className="inner_heading">
<h4>How big is your event?</h4>
</div>
<div className="row">
<div className="col-lg-8 offset-lg-2">
<div className="text_outer"># of Estimated Attendees</div>
<div className="row">
<div className="col-lg-4 col-md-4">
<div className="even_box1">
<input type="radio" className="ischeck" id="flexCheckDefault" checked={this.state.selectedOption==="1-20"} value="1-20" name="estimated" onChange={this.handleChange}/><h3>1-20</h3>
</div>
</div>
<div className="col-lg-4 col-md-4">
<div className="even_box1">
<input type="radio" className="ischeck" id="flexCheckDefault" checked={this.state.selectedOption==="21-50"} value="21-50" name="estimated" onChange={this.handleChange}/><h3>21-50</h3>
</div>
</div>
<div className="col-lg-4 col-md-4">
<div className="even_box1">
<input type="radio" className="ischeck" id="flexCheckDefault" value="50+" checked={this.state.selectedOption==="50+"} name="estimated" onChange={this.handleChange}/><h3>50+</h3>
</div>
</div>
</div>
<div className="text_outer atten">You can always edit it later</div>
<div className="btn_outer">
<button type='submit' className='btn btn-primary'> Continue </button>
{/* <button type='submit' className='btn btn-primary' disabled={btnDisabled} onClick={redirectNewStep}> Continue </button> */}
{/* Continue */}
</div>
</div>
</div>
</div>
</form>
</section>
</div>
)
}
}
export default connect()(Step5);
In this I am using radio button on submit how we check radio button is checked not. If check store their value in redux and local storage .
You are storing your selected value in selectedOption, retrieving that state in hadleSubmit should work fine:
handleSubmit = (e) => {
e.preventDefault();
const selected = this.state.selectedOption;
if (!selected) {
alert('Please select an estimate')
return
}
localStorage.setItem('step5', selected);
const data = {
id: new Date(),
step5: selected,
};
this.props.dispatch({ type: 'ADD_STEP5', data });
window.open('/Step6', '_self');
};
You are setting the value of selected radio button in the state variable selectedOption. You are assigning event.target.value to selectedOption, so selectedOption will either have value '21-50' or '50+' You just need to check if selectedOption is defined in your handleSubmit function
handleSubmit = (e) => {
e.preventDefault();
var step5 = this.state.selectedOption;
if(step5)
localStorage.setItem('step5', step5);
const data = {
id: new Date(),
step5:step5
}
this.props.dispatch({type:'ADD_STEP5',data});
window.open("/Step6" , "_self");
}

Render html based on the radio select

I have two radio buttons and need to render a different form for each when the user click the continue button, how can I do this? Here's the code I have so far.
class App extends Component {
constructor() {
super();
this.state = {
name: "typeOfInternet"
};
this.onChangeValue = this.onChangeValue.bind(this);
}
onChangeValue(event) {
console.log(event.target.value);
}
handleChange = () =>
this.setState({ showComponent: !this.state.showComponent });
render() {
return (
<Container>
<section className="section">
<div className="tittle">
<span>
Vamos começar? Escolha a modalidade e preencha seus dados para
continuar.
</span>
</div>
<div className="check-block" onChange={this.onChangeValue}>
<div className="check">
<input
type="radio"
id="residencia"
name="internet"
value="residencial"
/>
<label for="residencial">Internet Residencial</label>
</div>
<div className="check">
<input
type="radio"
id="empresa"
name="internet"
value="empresarial"
/>
<label for="empresarial">Internet Empresarial</label>
</div>
</div>
<div className="continue">
<button
className="btn-continue"
>
Continue
</button>
</div>
</section>
</Container>
)
}
}
export default App ;
The difference between codes are some input fields that "imternet Empresarial" has and "Internet Residencial" does not
There is no need for onChangeValue function because you're not calling it anywhere instead you can attach handleChange function to the <input> tags.
You should use the name which is provided in <input> inside state. So that it will be easier to use in setState.
There's no need to use .bind() also, with the help of arrow functions => you can escape that.
Inside switch, on different cases of the values instead of using <div>...</div>, you can call different components present in separate file. (in order to make the component readable)
Codesandbox Demo
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = {
internet: ""
};
}
handleChange = (e) => this.setState({ [e.target.name]: e.target.value });
renderForm = () => {
switch (this.state.internet) {
case "residencial":
return (
<div>
<h1>residencial form</h1>
</div>
);
case "empresarial":
return (
<div>
<h1>empresarial form</h1>
</div>
);
default:
return null;
}
};
render() {
return (
<section className="section">
<div className="tittle">
<span>
Vamos começar? Escolha a modalidade e preencha seus dados para
continuar.
</span>
</div>
<div className="check-block" onChange={this.onChangeValue}>
<div className="check">
<input
type="radio"
id="residencia"
name="internet"
value="residencial"
onChange={this.handleChange}
/>
<label htmlFor="residencial">Internet Residencial</label>
</div>
<div className="check">
<input
type="radio"
id="empresa"
name="internet"
value="empresarial"
onChange={this.handleChange}
/>
<label htmlFor="empresarial">Internet Empresarial</label>
</div>
</div>
{this.renderForm()}
<div className="continue">
<button className="btn-continue">Continue</button>
</div>
</section>
);
}
}
export default App;
First, you need to create your form element to render when radio buttons get toggled. Something like this:
const Form = ({ type }) => {
return type === "residencial" ? <div>Form 1</div> : <div>Form 2</div>;
};
Then keep track of the currently checked radio button and whether or not the continue button has been pressed. Something like this would work (Sandbox):
const Form = ({ type }) => {
return type === "residencial" ? <div>Form 1</div> : <div>Form 2</div>;
};
class App extends Component {
constructor() {
super();
this.state = {
name: "",
step: 1
};
}
onChangeValue = (event) => {
console.log(event.target.value);
this.setState({ name: event.target.value });
};
handleContinue = () => {
this.setState({ step: 2 });
};
render() {
const { step, name } = this.state;
return (
<div>
<section className="section">
<div className="tittle">
<span>
Vamos começar? Escolha a modalidade e preencha seus dados para
continuar.
</span>
</div>
<div className="check-block">
<div className="check">
<input
type="radio"
id="residencia"
name="internet"
value="residencial"
onChange={this.onChangeValue}
/>
<label for="residencial">Internet Residencial</label>
</div>
<div className="check">
<input
type="radio"
id="empresa"
name="internet"
value="empresarial"
onChange={this.onChangeValue}
/>
<label for="empresarial">Internet Empresarial</label>
</div>
</div>
<div className="continue">
<button
disabled={name === ""}
className="btn-continue"
onClick={this.handleContinue}
>
Continue
</button>
<hr />
{step === 2 ? <Form type={name} /> : null}
</div>
</section>
</div>
);
}
}
export default App;
There is also no need to bind your class functions, just use arrow functions, they will make your life much easier.

Append data in same page in button click with react Js

How can we list data from the array list on the same page in button click using react?
When user enter quantity setting value text box change event
class ProductList extends Component {
constructor(props) {
super(props);
this.state={
CartArray:[],
ProductList:[],
}
}
handleInputChange = event =>
{
const cart_values = event.target.name.split('-');
let newCart = {};
newCart["Key"]=cart_values[0]
newCart["ProductName"]=cart_values[1]
newCart["ProductBrand"]=cart_values[2]
this.setState(prevState => ({CartArray: [...prevState.CartArray, newCart]}))
}
viewCart = () => {
//What need to write here show data from CartArray:[] to my basket
}
}
Below is my render method. Numerical text box change i am setting in state value
render() {
return (
<div className="card" style={{ marginBottom: "10px"}}>
<div> <button className="btn btn-sm btn-warning float-right" onClick={this.viewCart}>View cart</button></div>
{this.state.ProductList.map((product, key) =>(
<div className="card-body">
<div className="card-title" key={key} value={key}>{product.ProductName}
<img src= {`data:image/jpeg;base64,${product.Image2}`} width="200" height="80" />{product.Brand}
<div>
<button className="btn btn-sm btn-warning float-right"
onClick={this.addToCart}>Add to cart</button>
<div>
<input type="number" min="0" pattern="[0-9]*" onInput={this.handleInputChange.bind(this)} name={`Name${key}-${product.ProductName}-${product.Brand}`} />
</div>
</div>
</div>
</div>
))}
</div>
)
}
If by show you mean to show on screen, not inside viewCart but in separate method render()
render(){
return(
<div>
{
this.state.CartArray.map((product) => {
<p>Key: {product.Key} </p>
<p>ProductName: {product.ProductName} </p>
<p>ProductBrand: {product.ProductBrand} </p>
})
}
</div>
);
}

How to move one object from one array to another on submit in React app

I'm building a simple hotel reviews app in React. I had it set up to display the first 3 reviews with the button "See all" to expand the box to see the rest of the reviews. However, I noticed that every time I try to submit a review, the last review in the first 3 reviews didn't show up in the rest of the reviews shown upon expansion. I tried a variety of solutions but couldn't solve it.
Here's my working demo: http://immense-beach-76879.herokuapp.com/
Here's my github repo: https://github.com/kikidesignnet/hotelreviews
Here is my code.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
// import '/css/style.css';
/* An example React component */
class App extends Component {
constructor(props) {
super(props);
//Initialize the state in the constructor
this.state = {
name: "",
reviews: [],
otherReviews: [],
open: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.renderReviews = this.renderReviews.bind(this);
this.toggleReviews = this.toggleReviews.bind(this);
// this.addReviews = this.addReviews.bind(this);
}
// handle change
handleChange(e) {
this.setState({
name: e.target.value
});
console.log('onChange', this.state.name);
}
handleSubmit(e) {
// stop browser's default behaviour of reloading on form submit
e.preventDefault();
axios
.post('/reviews', {
name: this.state.name
})
.then(response => {
console.log('from handle submit', response);
// set state
this.setState({
reviews: [response.data, ...this.state.reviews].slice(0,3),
name: "",
otherReviews: [response.data, ...this.state.reviews].slice(3).concat([...this.state.otherReviews])
});
});
}
renderReviews() {
return this.state.reviews.map(review => (
<div key={review.id} className="media innercard">
<div className="media-body">
<div className="profile_box">
<div className="profile_img">
<img src="/images/profilepic.png" alt="" />
</div>
<div className="profile_data">
<h3>{review.user.name}</h3>
<span className="text-muted">
{this.convertTime(review.updated_at)}
</span>
</div>
</div>
<p>{review.name}</p>
</div>
</div>
));
}
//get all the reviews from backend
getReviews(){
axios.get('/reviews').then(response => this.setState({
reviews: [...response.data.reviews].slice(0,3),
otherReviews: [...response.data.reviews].slice(3)
}));
}
toggleReviews() {
this.setState({open: !this.state.open});
}
//lifecycle method
componentDidMount(){
this.getReviews();
}
render() {
return (
<div className="container">
<div className="row justify-content-center">
<div className="col-md-8">
<div className="review_card">
<div className="card-body">
<form onSubmit={this.handleSubmit} className="comment_form">
<div className="form-group">
<i className="fas fa-comment-dots"></i>
<input
onChange={this.handleChange}
value = {this.state.name}
className="form-control"
placeholder="Comment"
required
/>
</div>
<div className="btn_align">
<button type="submit" className="btn red_btn">
Submit
</button>
</div>
</form>
<div className="reviews_list">
<div className="review_header">
<h1>Reviews</h1>
<button className="toggle_btn" onClick={this.toggleReviews}>{this.state.open ? 'Hide reviews' : 'See all'}</button>
</div>
{this.renderReviews()}
{this.state.open && this.state.otherReviews.map(review =>(
<div key={review.id} className="media innercard">
<div className="media-body">
<div className="profile_box">
<div className="profile_img">
<img src="/images/profilepic.png" alt="" />
</div>
<div className="profile_data">
<h3>{review.user.name}</h3>
<span className="text-muted">
{this.convertTime(review.updated_at)}
</span>
</div>
</div>
<p>{review.name}</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default App;
This is the code that I'm trying to edit to show the submitted review in the box.
handleSubmit(e) {
// stop browser's default behaviour of reloading on form submit
e.preventDefault();
axios
.post('/reviews', {
name: this.state.name
})
.then(response => {
console.log('from handle submit', response);
// set state
this.setState({
reviews: [response.data, ...this.state.reviews].slice(0,3),
name: "",
otherReviews: [response.data, ...this.state.reviews].slice(3).concat([...this.state.otherReviews])
});
});
}
How do I solve this and what am I doing it wrong?

setState is not updating the state even callback

On input field change, the handler function gets called and updates the state, but the state is not getting updated. I know setState() is async so I put the console.log in the callback.
Here are the functions:
class WorkspaceEditModal extends React.PureComponent {
constructor(props) {
super(props);
console.log(props.selectedWorkspace); // prints correct value
this.state = {
editWorkspaceName: props.selectedWorkspace,
exists: false,
};
}
handleEditChange = event => { // this handler is called
console.log(event.target.value); // prints new value
this.setState({ editWorkspaceName: event.target.value },
() => {console.log(this.state.editWorkspaceName)}); // still prints old value
};
handleFocus = e => e.target.select();
handleKeyPress = e => {
if (e.key === 'Enter') {
e.preventDefault();
this.editWorkspace();
}
};
render() {
const { editWorkspaceName, exists } = this.state;
let content;
content = (
<div className="save-workspace">
<div className="modal-header">
<div className="modal-title">
<p>Rename Workspace</p>
</div>
<div className="modal-select">
<span className="input-label">Workspace Name</span>
<div>
<input
className="name-input"
placeholder="Workspace Name"
type="text"
defaultValue={editWorkspaceName}
onChange={this.handleEditChange} // calls the handler on change
onFocus={this.handleFocus}
onKeyPress={this.handleKeyPress}
autoFocus
/>
{notValid && (
<p className="invalid">
Only alphanumeric characters, hyphens, spaces, and
underscores are allowed.
</p>
)}
</div>
</div>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-default"
onClick={this.onCloseModal}
>
Cancel
</button>
<button
type="button"
className="btn btn-primary"
onClick={this.editWorkspace}
disabled={notValid}
>
Update
</button>
</div>
</div>
);
const contentStyle = {
width: '500px',
};
return (
<Modal
isOpen={true}
onClose={this.onCloseModal}
contentStyle={contentStyle}
shouldCloseOnOverlayClick={true}
>
{content}
</Modal>
);
}
}
I don't know why this is happening. This is crazy.
Thank you for your help.
You are passing down the default value of the input via props. That should be your input default value. It should not update the state of the child component.
State should be updated on the parent component, not the child in this case. The handleEditChange (handleChange in example) function should be placed in the parent component, then passed down via props to the input. You want the props value changed in the parent via that parent function so that setState works properly.
You could use getDerivedStateFromProps, but it seems unnecessary here. https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html
class ParentWorkSpaceEditModal extends React.Component {
constructor(props) {
super(props);
this.state = {
editWorkspaceName: 'whatever your original value is',
exists: false,
};
}
handleChange = event => {
this.setState({ editWorkspaceName: event.target.value });
};
render() {
const { editWorkspaceName, exists } = this.state;
return (
<ChildWorkspaceEditModal editWorkspaceName={editWorkspaceName} exists={exists} handleChange={this.handleChange} />
)
}
}
const ChildWorkspaceEditModal = ({ editWorkspaceName, exists, handleChange )} => {
content = (
<div className="save-workspace">
<div className="modal-header">
<div className="modal-title">
<p>Rename Workspace</p>
</div>
<div className="modal-select">
<span className="input-label">Workspace Name</span>
<div>
<input
className="name-input"
placeholder="Workspace Name"
type="text"
value={editWorkspaceName}
onChange={handleChange}
/>
{notValid && (
<p className="invalid">
Only alphanumeric characters, hyphens, spaces, and underscores are allowed.
</p>
)}
</div>
</div>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-default"
onClick={this.onCloseModal}
>
Cancel
</button>
<button
type="button"
className="btn btn-primary"
onClick={this.editWorkspace}
disabled={notValid}
>
Update
</button>
</div>
</div>
);
const contentStyle = {
width: '500px',
};
return (
<Modal
isOpen={true}
onClose={this.onCloseModal}
contentStyle={contentStyle}
shouldCloseOnOverlayClick={true}
>
{content}
</Modal>
)
}

Resources