I would like to know how to accomplish this.
Passing an event from Parent to Child/Child, by this example: I have Apps.js, then FormDynamic.js and two Inputs. I want to pass handleChange from the Apps.js to FormDynamic.js and then to InputTextField.js and get the value from the App.js on the submitForm function.
//Apps.js
import React, {Component} from 'react';
import FormDynamic from './components/FormDynamic'
class App extends Component {
constructor(props)
{
super(props);
this.state={
fields: [
{id:"101", name:"101", placeholder:"Enter Value 1",input_type:"text",required:true},
{id:"102", name:"102", placeholder:"Enter Value 2",input_type:"number",required:true}
]
}
this._handleChange = this._handleChange.bind(this);
}
_handleChange = event =>{
this.setState({
[event.currentTarget.id]: event.currentTarget.value
});
};
submitForm = event =>{
const {fields, ...inputFields} = this.state;
console.log(fields);
console.log(inputFields);
event.preventDefault();
};
render(){
return (
<div className="App">
<FormDynamic fields={this.state.fields} handleChange={this._handleChange} />
<button onClick={this.submitForm}>Enviar Datos</button>
</div>
);
}
}
export default App;
//FormDynamic.js
import React, {Component} from 'react';
import InputTextField from './InputTextField'
import InputNumberField from './InputNumberField'
class FormDynamic extends Component
{
constructor(props)
{
super(props);
}
render()
{
return(
<div>
{this.props.fields.map(form => {
if (form.input_type ==="text")
{
return (
<InputTextField
id={form.id}
name={form.name}
placeholder={form.placeholder}
required={form.required}
key={form.id}
onChange = {this.props.handleChange}
/>
);
}
if (form.input_type ==="number")
{
return (
<InputNumberField
id={form.id}
name={form.name}
placeholder={form.placeholder}
required={form.required}
key={form.id}
onChange = {this.props.handleChange}
/>
);
}
return null;
})}
</div>
)
};
}
export default FormDynamic;
//InputTextField.js
import React from 'react';
const InputTextField = ({id,name,placeholder,required,_handleChange}) =>
(
<div>
<input type="text"
id={id}
name={name}
required={required}
placeholder={placeholder}
onChange={_handleChange}
/>
</div>
);
export default InputTextField;
From FormDynamic.js your passing props onChange to InputTextField.js but your reading wrong props _handleChange instead read onChange props, try the below code it will work.
//InputTextField.js
import React from 'react'
const InputTextField = ({id, name, placeholder, required, onChange}) => (
<div>
<input type="text"
id={id}
name={name}
required={required}
placeholder={placeholder}
onChange={onChange}
/>
</div>
)
export default InputTextField
Related
I am building a To-Do List web app with React as my first project.
I want to implement local storage which works fine only that,I am unable to handle check and uncheck of the checkbox prefectly.
Here is a link to the deployed website so you can understand the problem I am having.
https://rapture-todo.netlify.app/
When you add a todo, and mark it complete.
on reload, the checkbox of the todo is unchecked but the todo is marked complete.
Here is my source code[github link- https://github.com/coolpythoncodes/React-ToDo-List].
For App.js
import React, { Component } from 'react';
import Header from './component/Header';
import Info from './component/Info';
import AddToDo from './component/AddToDo';
import TodoListItem from './component/TodoListItem';
import './sass/main.scss';
class App extends Component{
constructor(props){
super(props);
this.state= {
value: '',
list: [],
show: true,
};
this.handleChange= this.handleChange.bind(this);
this.handleSubmit= this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
this.deleteTask = this.deleteTask.bind(this);
}
componentDidMount() {
const list = window.localStorage.getItem('userTodo') ? JSON.parse(localStorage.getItem('userTodo')) : [];
this.setState({ list })
}
handleChange(e) {
this.setState({value:e.target.value})
}
// Handle submission of user todo item
handleSubmit(e) {
e.preventDefault();
const newTask = {
id: Date.now(),
userTodo: this.state.value,
isCompleted: false,
checked: false,
}
// Validate form so user doesn't add an empty to do
if (this.state.value.length > 0) {
this.setState({
list: [newTask, ...this.state.list],
value: '', // Clear input field
show: true, // Success message
}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
}
// Handles checkbox
handleInputChange(id) {
this.setState({list: this.state.list.map(item => {
if (item.id === id) {
item.isCompleted = !item.isCompleted;
item.checked = !this.state.checked;
}return item
})}, ()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list));
})
}
// Delete a task
deleteTask(id){
this.setState({list: this.state.list.filter(item => item.id !== id )},()=>{
window.localStorage.setItem('userTodo', JSON.stringify(this.state.list))
})
console.log(this.state.list)
}
render(){
return(
<div>
<Header />
<Info />
<AddToDo onChange={this.handleChange} value={this.state.value} onSubmit={this.handleSubmit} />
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
</div>
)
}
}
export default App;
For TodoListItem.js
import React, { Component } from 'react';
import ToDoItem from './ToDoItem';
import '../sass/main.scss';
class ToDoListItem extends Component{
render(){
const {list, onChange, deleteTask, defaultChecked} = this.props;
return(
<div>
{list.map((todo)=>{
return (
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked}
/>
)
})}
</div>
)
}
}
export default ToDoListItem;
For TodoItem.js
import React, { Component } from 'react';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faTrashAlt } from '#fortawesome/free-solid-svg-icons'
import '../sass/main.scss';
class ToDoItem extends Component{
render(){
const {userTodo, isCompleted, onChange, id, deleteTask, defaultChecked} = this.props;
const checkStyle = isCompleted ? 'completed-todo' : 'not-completed-todo';
return(
<div className={`container ${checkStyle}`}>
<input type="checkbox" onChange={onChange.bind(this, id)} defaultChecked={defaultChecked}/>
<div >
<p className='title'>{userTodo}</p>
</div>
{/* Delete button */}
<button onClick={deleteTask.bind(this, id)}><FontAwesomeIcon className='remove-icon' icon={faTrashAlt} /></button>
</div>
)
}
}
export default ToDoItem;
Please note: I have gone through other questions similar to the problem I am having but I could not solve this problem.
If I did not state the question well, please let me know.
In the below code in App.js,
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list} defaultChecked={this.state.checked} />
You are setting, defaultChecked={this.state.checked} Why do you do that? There is nothing called checked in the state.
In fact, there is no need to pass the defaultValue.
Make the following changes,
In App.js, remove defaultValue prop for TodoListItem
<TodoListItem deleteTask={this.deleteTask} onChange={this.handleInputChange} list={this.state.list}/>
In TodoListItem.js, remove defaultChecked={defaultChecked}
<ToDoItem
key={todo.id}
userTodo={todo.userTodo}
isCompleted={todo.isCompleted}
onChange={onChange}
id={todo.id}
deleteTask={deleteTask}
defaultChecked={defaultChecked} // Remove this.
/>
In ToDoItem.js,
<input type="checkbox"onChange={onChange.bind(this, id)}
defaultChecked={isCompleted} // Replace defaultValue with isCompleted
/>
I'm using react-hook-form and trying to pass the data from a form to its parent.
For this I'm trying to pass the data via a callback, but it's not working.
When I console.log data inside the parent component, I get undefined.
Parent component
import React from 'react';
import InputForm from './InputForm';
const App = (data) => {
const onFormSubmit = () => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={() => onFormSubmit()} />
</div>
);
};
export default App;
Child component
import React from 'react';
import { useForm } from 'react-hook-form';
const InputForm = ({ onceSubmited }) => {
const { register, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
onceSubmited(data);
};
return (
<>
<form onSubmit={handleSubmit(onSubmit)}>
<input
type='text'
name='name'
ref={register}
/>
<input
type='email'
name='email'
ref={register}
/>
<button type='submit'>
Submit
</button>
</Form>
</>
);
};
export default InputForm;
You need to pass the argument in your arrow function. This should make it work:
import React from 'react';
import InputForm from './InputForm';
const App = () => {
const onFormSubmit = (data) => {
console.log(data.name);
};
return (
<div className='App'>
<InputForm onceSubmited={(data) => onFormSubmit(data)} />
</div>
);
};
export default App;
I'm trying to get a reaction from onInputChange function when I use it with onChange on input, but I didn't get why it's not console.log the value. Any help?
App.js
import React, { Component } from 'react';
import Output from './Components/Output';
import NumberInput from './Components/NumberInput';
import './App.css';
class App extends Component {
constructor() {
super()
this.state = {
decimal: 0,
inputString: ''
}
}
onInputChange = (event) => {
console.log(event.target.value)
}
render() {
const { decimal, inputString } = this.state;
return (
<fragment>
<h1>Binary to Decimal convertor</h1>
<NumberInput InputChange={this.onInputChange} />
<button>Submit</button>
<Output string={inputString}/>
</fragment>
);
}
}
export default App;
NumberInput component:
import React from 'react';
const NumberInput = ({ inputChange }) => {
return (
<fragment>
<input type='search' onChange={inputChange}></input>
</fragment>
)
}
export default NumberInput;
InputChange should be camelcase
change
<NumberInput InputChange={this.onInputChange} />
to
<NumberInput inputChange={this.onInputChange} />
you may also need to bind
<NumberInput inputChange={(e) => this.onInputChange(e)} />
Need some changes
Change your InputChange={this.onInputChange}
to inputChange={this.onInputChange} in App component.
because you are accessing inputChange in your NumberInput component.
And also change your fragment tag as well because you used fragment instead of Fragment tag.
Either use <Fragment>, <React.Fragment> or <> in NumberInput, App component like-
<Fragment>
<input type='search' onChange={inputChange}></input>
</Fragment>
How can I translate this uncontrolled component to react hooks (and avoid multi render of the input field):
class MyForm extends React.Component {
constructor(props) {
super(props);
this.input = React.createRef();
}
render() {
const {onInputChanged} = this.props;
return (
<form>
<input type="text" ref={this.input} onChange={onInputChanged}/>
</form>
);
}
}
If you want to convert your component to a functional component and use hooks you can do this way:
import React, { useRef,useEffect } from 'react';
useEffect(() => {
},['the props you write here will trigger the re-render when they change'])
const MyForm = (props) => {
const input = useRef(null);
return (
<form>
<input type="text" ref={input} onChange={props.onInputChanged} />
</form>
);
}
export default MyForm;
Take a look at the useRef hook: https://reactjs.org/docs/hooks-reference.html#useref
import React, { useRef } from 'react';
export const MyForm = (props) => {
const input = useRef(null);
return (
<form>
<input type="text" ref={input} onChange={props.onInputChanged} />
</form>
);
}
I intend to create a to-do list without using ref as in the many examples, but it isn't working.
The expected behavior is that upon entering an entry, it will show up at the top and upon clicking add, it will create an input box for entering an entry. Currently, upon entering the state returns undefined.
The code can be found below or in this sandbox:
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import ToDo from './todo'
class App extends Component {
constructor() {
super();
this.state = {
todos: []
};
}
onChange=(e)=>{
const newToDos = [...this.state.todos]
newToDos.push(e.target.value)
this.setState({
todos: newToDos
})
}
onAdd=(e)=>{
e.preventDefault();
const newtodos=[...this.state.todos]
this.setState({
todos: newtodos.push("")
})
}
render() {
console.log(this.state.todos)
return (
<div>
<p>All the to-dos include {this.state.todos}</p>
<ToDo
todos={this.state.todos}
/>
<form onSubmit={this.onChange}>
<input
type="text"
placeholder="add a new todo..."
/>
</form>
<button onClick={this.onAdd}>+</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
And here is the todo.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
export default class ToDo extends Component {
constructor(props){
super(props)
}
render() {
const {todos, onChange}=this.props
return (
<div>
{
todos.map((todo, index)=>
<div>
{todo}
</div>
)
}
</div>
);
}
}
You can store your new todo in state when onChange input and add this into todos when click save.
I have forked and edit your sample.
https://stackblitz.com/edit/react-nwtp5g?file=index.js
BTW: In your sample, newtodos.push("") will return the length of newtodos array, not the array after pushed.
onAdd=(e)=>{
e.preventDefault();
const newtodos=[...this.state.todos]
this.setState({
todos: newtodos.push("")
})
Hope this help.
your code newtodos.push("") dosent return array so no map function:
this.setState({
todos: newtodos.push("")
})
correct it something like this
this.setState({
todos: newtodos.concat("new value")
})
You have a problem with this code,
<form onSubmit={this.onChange}>
<input
type="text"
placeholder="add a new todo..."
/>
</form>
Here you are adding onSubmit on form, which will never call because you don't have submit button.
you should do something like this,
<form>
<input
type="text"
placeholder="add a new todo..."
onChange={this.onChange}
value={this.state.currentValue}
/>
</form>
onChange=(e)=>{
event.preventDefault();
this.setState({
currentValue: e.target.value
})
}
onAdd=(e)=>{
e.preventDefault();
const newToDos = [...this.state.todos]
newToDos.push(this.state.currentValue)
this.setState({
todos: newToDos,
currentValue: ''
})
}
Demo
Update
In your todo component you have useless constructor, If you don't have state in a component or don't have any function to bind this don't add constructor.
You can remove the constructor.
Another thing is, you are not passing any onChange prop to todo component, so here you will get undefined for onChange.
const {todos, onChange}=this.props
You can also write this component as a functional component.
You can update your code with
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import ToDo from './todo'
class App extends Component {
constructor() {
super();
this.state = {
todos: [],
inputText: ""
};
}
onAdd= () => {
this.setState({
todos: [...this.state.todos, this.state.inputText], textInput: ""
})
}
render() {
console.log(this.state.todos)
return (
<div>
<p>All the to-dos include {this.state.todos}</p>
<ToDo
todos={this.state.todos}
/>
<form>
<input
type="text"
placeholder="add a new todo..."
onChange={inputText => this.setState({inputText})}
/>
</form>
<button onClick={this.onAdd}>+</button>
</div>
);
}
}
render(<App />, document.getElementById('root'));
And in todo.js you can simply do
import React, { Component } from 'react';
import { render } from 'react-dom';
export default const ToDo = ({todos}) => {
return(<div>
{todos.map((todo, index) => (
<div key={index}>
{todo}
</div>))}
</div>)}
as it do not contains any state associated with it.