hello I am novice on react, I have this form and I would like that my add button appears only when there is an input on the input.
I tried this in my render. thanks
class App extends Component {
state= {
value:''
}
handleChange=(e)=>{
e.preventDefault()
this.setState({value: e.currentTarget.value})
}
handleAdd=(e)=>{
e.preventDefault()
let value= [ ...this.state.value]
value.push(this.state.value)
}
render () {
let button;
if(this.handleChange){
button=<button>Add</button>
}
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={this.state.value} onChange={this.handleChange}/>
</label>
{button}
</form>
</div>
)
}
}
You can use this.
render () {
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={this.state.value} onChange={this.handleChange}/>
</label>
{
this.state.value !== ''&&
( <button>Add</button> )
}
</form>
</div>
)
}
}
I think it could be worked on your case.
Here are two ways you can handle this.. (make sure to expand the snippets and run them to see the code and how it works).
This is the more straight forward way:
const { Component } = React;
class App extends Component {
state = {
value: "",
added: []
};
handleChange = e => {
e.preventDefault();
this.setState({ value: e.currentTarget.value });
};
handleAdd = e => {
e.preventDefault();
this.setState({
added: [...this.state.added, this.state.value],
value: ""
});
};
render() {
let button;
let items;
if(this.state.value) {
button = <button>Add</button>
}
if(this.state.added && this.state.added.length > 0) {
items = (
<div>
<h3>Added Items:</h3>
<ul>{this.state.added.map(i => <li>{i}</li>)}</ul>
</div>
)
}
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={this.state.value} onChange={this.handleChange} />
</label>
{button}
</form>
{items}
</div>
);
}
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
This is the exact same as above, only using different (more efficient) syntax:
const { Component } = React;
class App extends Component {
state = {
value: "",
added: []
};
handleChange = e => {
e.preventDefault();
this.setState({ value: e.currentTarget.value });
};
handleAdd = e => {
const { value, added } = this.state;
e.preventDefault();
this.setState({
added: [...added, value],
value: ""
});
};
render() {
const { value, added } = this.state;
let button = value && <button>Add</button>;
let items = added && added.length > 0 && (
<div>
<h3>Added Items:</h3>
<ul>{added.map(i => <li>{i}</li>)}</ul>
</div>
);
return (
<div className="formulaire">
<form onSubmit={this.handleAdd}>
<label>
<p>Name:</p>
<input value={value} onChange={this.handleChange} />
</label>
{button}
</form>
{items}
</div>
);
}
}
ReactDOM.render(<App />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
Related
I'm learning React and I created a simple todo list app and I'm trying to erase the input field as I did for my onClick function on my keypress function. However, it doesn't render the same when I use setTodoInput(""); on that keypress function. It only shows the first character of the input. If I comment out setTodoInput(""); out of the keypress function, it works fine, but the input field doesn't erase. I don't understand why although I have a controlled input, it doesn't function the same. if someone can please explain, it would be appreciated.
this is my code for my App file:
import React, { useState } from "react";
import InputArea from "./InputArea";
import ToDoTask from "./ToDoTask";
function App() {
const [todoTasks, setTodoTasks] = useState([]);
function addTask(todoInput) {
setTodoTasks((prevTodoTasks) => {
return [...prevTodoTasks, todoInput];
});
}
function handleKeyPress(event) {
if (event.key === "Enter") {
setTodoTasks((prevTodoInput) => {
const newTodoInput = event.target.value;
return [...prevTodoInput, newTodoInput];
});
// const newTodoInput = event.target.value;
// setTodoTasks((prevTodoTasks) => {
// console.log(newTodoInput);
// return [...prevTodoTasks, newTodoInput];
// });
// }
}
}
function deleteTodoTask(id) {
setTodoTasks((prevTodoTasks) => {
return prevTodoTasks.filter((task, i) => {
return i !== id;
});
});
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<div className="form">
<InputArea onAdd={addTask} onKeyPress={handleKeyPress} />
</div>
<div>
<ul>
{todoTasks.map((todoTasks, i) => (
<ToDoTask
key={i}
id={i}
text={todoTasks}
onChecked={deleteTodoTask}
/>
))}
</ul>
</div>
</div>
);
}
export default App;
I also created an input component:
import React, { useState } from "react";
function InputArea(props) {
const [todoInput, setTodoInput] = useState("");
function handleChange(event) {
const newInput = event.target.value;
setTodoInput(newInput);
}
return (
<div className="form">
<input
onKeyDown={(event) => {
props.onKeyPress(event);
setTodoInput("");
}}
onChange={handleChange}
type="text"
value={todoInput}
/>
<button
onClick={() => {
props.onAdd(todoInput);
setTodoInput("");
}}
>
<span>Add</span>
</button>
</div>
);
}
export default InputArea;
this is my todoTask component:
import React from "react";
function ToDoTask(props) {
return (
<div
onClick={() => {
props.onChecked(props.id);
}}
>
<li>{props.text}</li>
</div>
);
}
export default ToDoTask;
If the goal is to clear the input when "enter" is pressed then I suggest using a form element. So long as there is just the one input then pressing enter while focused will submit the form. Use the form's submit handler to call the onAdd callback and reset the local todoInput state.
InputArea
function InputArea({ onAdd }) {
const [todoInput, setTodoInput] = useState("");
const submitHandler = (e) => {
e.preventDefault();
if (todoInput) {
onAdd(todoInput);
setTodoInput("");
}
};
function handleChange(event) {
const { value } = event.target;
setTodoInput(value);
}
return (
<form onSubmit={submitHandler}>
<input onChange={handleChange} type="text" value={todoInput} />
<button type="submit">
<span>Add</span>
</button>
</form>
);
}
Demo
function InputArea({ onAdd }) {
const [todoInput, setTodoInput] = React.useState("");
const submitHandler = (e) => {
e.preventDefault();
if (todoInput) {
onAdd(todoInput);
setTodoInput("");
}
};
function handleChange(event) {
const { value } = event.target;
setTodoInput(value);
}
return (
<form onSubmit={submitHandler}>
<input onChange={handleChange} type="text" value={todoInput} />
<button type="submit">
<span>Add</span>
</button>
</form>
);
}
function ToDoTask(props) {
return (
<div
onClick={() => {
props.onChecked(props.id);
}}
>
<li>{props.text}</li>
</div>
);
}
function App() {
const [todoTasks, setTodoTasks] = React.useState([]);
function addTask(todoInput) {
setTodoTasks((prevTodoTasks) => {
return [...prevTodoTasks, todoInput];
});
}
function deleteTodoTask(id) {
setTodoTasks((prevTodoTasks) => {
return prevTodoTasks.filter((task, i) => {
return i !== id;
});
});
}
return (
<div className="container">
<div className="heading">
<h1>To-Do List</h1>
</div>
<div className="form">
<InputArea onAdd={addTask} />
</div>
<div>
<ul>
{todoTasks.map((todoTasks, i) => (
<ToDoTask
key={i}
id={i}
text={todoTasks}
onChecked={deleteTodoTask}
/>
))}
</ul>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
I'm new to the Typescript and I'm getting error when I'm trying to console.log input field value.
Any suggestions?
Code :
class Register extends Component<{},userState> {
state = {
userList : []
}
emailEl= createRef<HTMLInputElement>()
passwordEl= createRef<HTMLInputElement>()
confirmpasswordEl= createRef<HTMLInputElement>()
registerUser = () => {
const {userList} = this.state
const destructList = [...userList]
// const newUser = [destructList, {
// email:this.emailEl.current.value,
// password:this.passwordEl.current.value
//
// }]
console.log('--------destructList', this.emailEl.current);
}
render() {
return (
<div className="container">
<h1 className={'registerH1'}>Registration</h1>
<div className="email">
<input ref={this.emailEl} type={'email'} placeholder={'Enter your E-mail'} />
</div>
<div ref={this.passwordEl} className="password">
<input type={'password'} placeholder={'Enter your password'} />
</div>
<div ref={this.confirmpasswordEl} className="confirmPassword">
<input type={'password'} placeholder={'Confirm your password'} />
</div>
<div id={'buttons'} className="buttons">
<Link to={'/login'} >
<button>Log In</button>
</Link>
<button onClick={() => this.registerUser()}>Registration</button>
</div>
</div>
)
}
and here is the error :
TS2531: Object is possibly 'null'
You will need to use this.emailEl.current according to the react documentation. You might also need to guard against null values to make typescript happy.
import * as React from 'react'
class MyComponent extends React.Component {
state = { userList: [] }
emailEl = React.createRef<HTMLInputElement>()
registerUser = () => {
const { userList } = this.state
if (this.emailEl.current) {
console.log('--------', this.emailEl.current.value)
}
}
render() {
return <div></div>
}
}
Its a type error, try:
emailEl= createRef<HTMLInputElement | null>();
// or
emailEl= createRef<HTMLInputElement>(null);
registerUser = () => {
if (emailEl && emailEl.current) {
// ts knows value not null
this.emailEl.current.value;
}
}
See the docs of React.createRef
Set the attributes of a input field or any component by taking input from the user dynamically?
I would like to know if there is any way, where I would give user an option to choose a component from the list of components i would mention, and allow him to customize the components attributes. For example if the user chooses a Input component, he must be able to set the attributes of that particular component, like "required", "type", "placeholder".
You can achieve it by passing all attributes you want as props to the child component.
You should also add them to state of parent component with change handler.
Each time the user change something of the attributes, the state should update.
As the state updates, the new state will pass as props to child Component and it'll update.
I made a simple example to input: You can change its placeholder, minLength, and requierd.
Check This Example
in the render, method you can do something like this
render() {
// change the name and values base on your user input
userInputtedAttribName = "placeholder";
userInputtedAttribValue = "the placeholder";
// the object to contain your user defined attribute name and values
const dynamicAttributes = {
[userInputtedAttribName]: userInputtedAttribValue
};
return (
<div>
<input type="text" {...dynamicAttributes}></input>
</div>
)
}
the spread operator, {...dynamicAttributes}, will build the attributes and their values dynamically
Probably not even what you're looking for, but I made a medium-sized prototype that can show you how to create Components (input, button, textarea), dynamically.
It's like filling out a form. Choose a type of component you want to make from the select-list. Then define the attributes you want in the proceeding textboxes. Once you're done adding all the attributes, hit Generate to render your customized component.
Sandbox: https://codesandbox.io/s/dynamic-component-generator-mhuh5
Working code:
import React from "react";
import ReactDOM from "react-dom";
import Input from "./Input";
import Button from "./Button";
import TextArea from "./TextArea";
import "./styles.css";
class ComponentGenerator extends React.Component {
state = {
componentInProgress: null,
customizeMode: false,
attributeName: "",
attributeSetting: "",
boolean: false,
builtComponents: []
};
handleSelection = e => {
this.setState({
componentInProgress: { componentType: e.target.value },
customizeMode: true
});
};
createOptions = () => {
const { customizeMode, componentInProgress } = this.state;
return (
<div>
<h4>Choose a Component:</h4>
<select
onChange={this.handleSelection}
value={!customizeMode ? "Select" : componentInProgress.componentType}
>
<option>Select</option>
<option>Input</option>
<option>TextArea</option>
<option>Button</option>
</select>
</div>
);
};
handleOnChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
handleOnSubmit = e => {
const {
attributeName,
attributeSetting,
boolean,
componentInProgress
} = this.state;
e.preventDefault();
let componentCopy = JSON.parse(JSON.stringify(componentInProgress));
componentCopy.props = {
...componentCopy.props,
[attributeName]: boolean ? boolean : attributeSetting
};
this.setState({
componentInProgress: componentCopy,
attributeName: "",
attributeSetting: "",
boolean: false
});
};
setBoolean = boolean => {
this.setState({
boolean: boolean
});
};
generateComponent = () => {
const { componentInProgress, builtComponents } = this.state;
this.setState({
componentInProgress: null,
customizeMode: false,
builtComponents: [...builtComponents, componentInProgress]
});
};
defineComponentAttributes = () => {
const {
componentInProgress,
attributeName,
attributeSetting,
boolean
} = this.state;
return (
<div>
<h4>
Customizing:{" "}
<span className="highlight">{componentInProgress.componentType}</span>
</h4>
{/*Render form */}
<form onSubmit={this.handleOnSubmit}>
<label>Attribute: </label>
<input
className="form-group"
onChange={this.handleOnChange}
value={attributeName}
name="attributeName"
placeholder="Choose attribute (type)"
/>
<label>Definition: </label>
<input
className="form-group"
onChange={this.handleOnChange}
value={attributeSetting}
name="attributeSetting"
placeholder="Define attribute (text)"
/>
<label>This is a Boolean type: </label>
<input
type="radio"
name="boolean"
onChange={() => this.setBoolean(true)}
/>
True
<input
type="radio"
name="boolean"
checked={boolean === false}
onChange={() => this.setBoolean(false)}
/>
False
<button className="form-group" type="submit">
Add
</button>
</form>
{/*Create List of attributes */}
{componentInProgress.props && (
<div>
<h4>Defined Attributes:</h4>
{Object.entries(componentInProgress.props).map(
([propName, propValue]) => {
return (
<div key={propName}>
<span>{propName}: </span>
<span>{propValue + ""}</span>
</div>
);
}
)}
</div>
)}
<div>
<h4>Click to finish and generate:</h4>
<button onClick={this.generateComponent}>Generate</button>
</div>
</div>
);
};
renderComponents = () => {
const { builtComponents } = this.state;
return builtComponents.map((component, index) => {
let renderedComponent = () => {
switch (component.componentType) {
case "Input":
return <Input {...component.props} />;
case "Button":
return <Button {...component.props} />;
case "TextArea":
return <TextArea {...component.props} />;
default:
return null;
}
};
return (
<div key={index} className="componentSection">
<h4>{component.componentType}</h4>
{renderedComponent()}
<div>
<p>Attributes: </p>
{Object.entries(component.props).map(([propName, propValue]) => {
return (
<div key={propName}>
<span>{propName}: </span>
<span>{propValue + ""}</span>
</div>
);
})}
</div>
</div>
);
});
};
render() {
const { customizeMode } = this.state;
return (
<div>
{this.createOptions()}
{customizeMode && this.defineComponentAttributes()}
<div className="components">{this.renderComponents()}</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<ComponentGenerator />, rootElement);
I'm building a web application to manage some inputs from the user where I want to execute a function on every object in list that is rendered in react. The rendered objects are a different class than the one it is executed in.
import React, { Component } from "react";
import UserInput from "./UserInput";
class Layout extends Component {
objList = []
state = {
update: ""
}
anotherOne = async () => {
this.objList.push(<UserInput key={this.objList.length} />);
this.setState({update: ""});
}
submitCase = async () => {
for (var testCase in this.objList){
this.objList[0].submitInfo();
}
}
removeLatest = async () => {
this.list.pop();
this.setState({update: ""});
}
render(){
return(
<div id="container">
<div>
{ this.objList }
</div>
<div>
<button onClick={ this.anotherOne }>Another One</button>
<button onClick={ this.submitCase }>Submit</button>
</div>
</div>
);
}
}
export default Layout;
import React, { Component } from "react";
class UserInput extends Component {
state = {
name: "",
hairColor: "",
age: ""
}
submitInfo = async () => {
let path = '/dbmanager';
let apiName = "myApi"
let myInit = {
body: {categoryId: "Person", type: this.state.hairColor, data: JSON.stringify(this.state)},
contentType: 'application/json',
}
await API.post(apiName, path, myInit)
.then(response => {
// Add your code here
console.log(response);
})
.catch(error => {
console.log(error.response);
})
}
handleStateUpdate = (event) => {
var eName = event.target.name;
var eValue = event.target.value;
this.setState({[eName]: eValue});
console.log(event.target.value, event.target.name);
}
render(){
return(
<div id="container">
<div>
<label>Name: </label>
</div>
<textarea
type="text"
id="name"
name="name"
value={this.state.name}
onChange={this.handleStateUpdate}/>
<div>
<label>Hair Color: </label>
</div>
<textarea
type="text"
id="hairColor"
name="hairColor"
value={this.state.hairColor}
onChange={this.handleStateUpdate}/>
<div>
<label>Age: </label>
</div>
<textarea
type="text"
id="age"
name="age"
value={this.state.age}
onChange={this.handleStateUpdate}/>
</div>
);
}
}
export default UserInput;
I want the access to the class functions of UserInput so that I could submit the data from all of them on the same button press. Instead the objects are considered functions and are not executable in any means.
How to check if each state has value then combine all values?
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
inputvalue : '',
allval: ''
}
}
onChangeOfInput =(name,value) =>{
this.setState({
[name]: value
});
}
getValues = () =>{
console.log(this.state);
if(this.state.Title1) {
this.setState({
allval: this.state.allval+this.state.Title1
});
}
}
render() {
return (
<div className="hello">
<Input onChangeOfInput={this.onChangeOfInput}
placeholder="Title 1" name="Title1" />
<br/>
<Input placeholder="Title 2" name="Title2" onChangeOfInput={this.onChangeOfInput} />
<br/>
<Input placeholder="Title 3" name="Title3" onChangeOfInput={this.onChangeOfInput}/>
<br/>
<Input placeholder="Title 4" name="Title4" onChangeOfInput={this.onChangeOfInput}/>
<br/>
<button onClick={this.getValues}>Get value</button>
</div>
)
}
}
class Input extends React.Component {
constructor(props) {
super(props)
this.state = {
inputvalue: ''
}
}
handleChange(e) {
this.setState({
inputvalue: e.target.value
});
this.props.onChangeOfInput(this.props.name,e.target.value)
}
render() {
return (
<input
type="text"
placeholder={this.props.placeholder}
value={this.state.inputvalue}
onChange={this.handleChange.bind(this)}
/>
)
}
}
ReactDOM.render(<App />, document.querySelector("#app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
jsfiddle: https://jsfiddle.net/vxm2ojLz/
The issue is here, I need to check each value state.Title1, state.Title2, state.Title3, state.Title4 if they are not empty, then I want to combine all values if it is not empty and assign the combined values to allVal, how to combine all values to allval? Thanks
You need to be doing something like this.
getValues = () => {
console.log(this.state);
let combinedString = "";
Object.keys(this.state)
.map( igKey => {
if(this.state[igKey] != "" && igKey.includes('Title')){
combinedString = combinedString +''+ this.state[igKey];
return combinedString
}
});
this.setState({allval:combinedString})
console.log(combinedString);
}
working fiddle https://jsfiddle.net/2nhc6drm/
hope this helps!
Try handling getValues like this:
getValues = () =>{
console.log(this.state);
let result = [];
Object.keys(this.state).forEach(key => {
if (key.includes('Title') && this.state[key]) result.push(`${key}: ${this.state[key]}`);
})
this.setState({
allval: result.join('; ')
})
}
Please Update getValues method :-
For concatination,it will ignore the keys allval and inputval.
getValues = () => {
let allval = ''
for(let key of Object.keys(this.state)){
if(key==='allval' || key==='inputval'){
continue;
}
else{
let value=this.state[key];
console.log(value);
if(value===''){
}
else{
allval=allval+value;
}
console.log(allval);
}
}
this.setState({allval:allval})
}
Working SandBox :- https://codesandbox.io/s/vqoxo9w1wy
Hope this helps,
Cheers !!
I'd recommend to use reduce for combinde the values, and use the functional setState to avoid double state change:
class App extends React.Component {
state = {
allVal: '',
title1: '',
title2: ''
}
getValues = (prevState, name, newVal) => {
return Object.keys(prevState)
.reduce((acc, key) => {
if (key === 'allVal') return acc;
if (key === name) return acc + newVal;
return acc + prevState[key];
}, '')
}
handleChange = ({ target: { name, value } }) => {
this.setState(prevState => ({
[name]: value,
allVal: this.getValues(prevState, name, value)
}))
}
render(){
const { title1, title2, allVal } = this.state;
return (
<div>
<input name="title1" onChange={this.handleChange} value={title1} /><br />
<input name="title2" onChange={this.handleChange} value={title2} /><br />
allVal: <span>{allVal}</span>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>