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>
Related
I am creating To do list app in React. It reads the input field correctly from state, but, when I added file, which reads all the information from input and outputs it as a new div with the name of todo item, it shows an error - "TypeError: Cannot destructure property 'name' of 'props.todo' as it is undefined".
import React, {useState} from 'react';
import './App.css';
import {Container, Row, Col} from 'react-bootstrap';
import AddForm from './components/AddForm';
import TodoList from './components/TodoList';
function App() {
const [todos, addTodos] = useState([]);
const addTodoHandler = todo => addTodos([...todos, todo]);
return (
<Container fluid>
<Row>
<h1>Todo list</h1>
<Col>
<TodoList todos={todos} />
</Col>
<Col>
<AddForm addTodoHandler={addTodoHandler}/>
</Col>
</Row>
</Container>
);
}
export default App;
import React from 'react';
const TodoList = (props) => {
const {name} = props.todo;
return (
<div>
<div>{name} </div>
<button>Delete</button>
</div>
);
}
export default TodoList;
import React from "react";
import { Button } from "react-bootstrap";
class AddForm extends React.Component {
state = {
name: "",
};
add = (e) => {
e.preventDefault();
if (this.state.name === "") {
alert("Todo cannot be empty");
return;
}
this.props.addTodoHandler(this.state);
alert(this.state.name)
this.setState({ name: "" });
};
render(){
return (
<div>
<h2>Add Todo</h2>
<form onSubmit={this.add}>
<input
type="text"
name="todo"
value={this.state.name}
onChange={(e) => this.setState({ name: e.target.value })}
/>
<Button type="submit">Add</Button>
</form>
</div>
);
}
};
export default AddForm;
As pointed out in comments you need to fix the destructuring statement to use the correct prop (todos instead of todo).
But you have an additional problem in that your <TodoList> component is not set up to render a list. You're passing todos as an array prop to <TodoList> but in <TodoList> you're using it as a single object. You'll need to fix that as well. If you want a list to be rendered you'll need to iterate over it. Something like this:
import React from 'react';
const TodoList = (props) => {
const list = props.todos || [];
return list.map(item =>
<div>
<div>{item.name} </div>
<button>Delete</button>
</div>
);
}
export default TodoList;
Note that this also checks for props.todos to be undefined and if so assigns list to be an empty array so that the render has a value and does not crash.
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 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
I'm creating a ReactJS package. I've created a component called Form which contains a normal form. When calling it on the App.js I add components on it which called TextField. So, the code on App.js is:
import React, { Component } from 'react'
import { Form, TextField } from 'form2'
import './App.css'
export default class App extends Component {
render () {
return (
<div>
<Form>
<TextField placeholder="Type your name..." />
<TextField placeholder="Type your email..." />
</Form>
</div>
)
}
}
And the code of the Form component is:
import React, { Component } from 'react'
class Form extends Component {
constructor(props) {
super(props)
this.state = {
values: {}
}
}
renderChildren = () => {
return this.props.children;
};
render() {
return <form>
{ this.renderChildren() }
</form>
}
}
export default Form
And this is mt TextField component:
import React, { Component } from 'react'
class TextField extends Component {
constructor(props) {
super(props);
this.state = {
value: null,
};
}
onChange = ({ target }) => {
this.setState({ value: target.value });
};
render() {
return <input onChange={this.onChange} {...this.props}>
{this.state.value}
</input>
}
}
export default TextField;
The problem is happening when I type something on the input, the inputs disappeared and got this error message on the console:
The above error occurred in the <input> component
and this one
Uncaught Invariant Violation: input is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
Change:
render() {
return <input onChange={this.onChange} {...this.props}>
{this.state.value}
</input>
}
To:
render() {
return <input defaultValue={this.state.value} onChange={this.onChange} {...this.props} />
}
Or (depending on the behavior your want):
render() {
return <input value={this.state.value} onChange={this.onChange} {...this.props} />
}
The existing code is trying to set the child of input to {this.state.value}.
My question, while related to a specific use case, is more about a general concept.
I use a library Informed for form management and it uses higher order components to allow creating custom fields. Here's the HOC:
import React from 'react';
import Field from '../components/Field';
function getDisplayName(WrappedComponent) {
return WrappedComponent.displayName || WrappedComponent.name || 'Component';
}
const asField = Component => {
const displayName = getDisplayName(Component);
Component.displayName = 'Wrapper';
const AsField = props => <Field component={Component} {...props} />;
AsField.displayName = displayName;
return AsField;
};
export default asField;
I took this from the official repo. So here's how it's supposed to be used:
// Definition
const CustomInput = asField(
({
fieldState: {value},
fieldApi: {setTouched, setValue},
forwardedRef
}) =>
<input onFocus={() => setTouched()} onChange={e => setValue(e.target.value)}
ref={forwardedRef} value={!value && value !== 0 ? '' : value} />
)
// Usage
<CustomInput field="firstName" />
So in my case, first, I'm not a big fan of higher order components, and I don't like specifying the field name using field prop, I prefer name. So with that in mind I create a component that duplicates the functionality of the asField HOC and uses name instead of field:
//#flow
import React from 'react'
import {asField} from 'informed'
export const Field = ({children, name, ...props}) => React.createElement(
asField(children),
{
field: name,
...props
}
)
Then I use it like this:
// Definition
const CustomInput = ({name}) =>
<Field field={name}>
{({
fieldState: {value},
fieldApi: {setTouched, setValue},
forwardedRef
}) =>
<input onFocus={() => setTouched()} onChange={e => setValue(e.target.value)}
ref={forwardedRef} value={!value && value !== 0 ? '' : value} />
}
</Field>
// Usage
<CustomInput name="firstName" />
This works, but interestingly, it causes something in the state to endlessly update something in the state which blocks the whole UI. The documented way works just fine.
I cannot find anything in my component. The component that asField returns is the one that accepts the field prop, it gets it through my function along with other props.
What could be the issue here? Did I miss something or is there a problem with the core concept?
you are using the wrong syntax for createElement
instead of createElement({}) use createElement(comp, props, children)
I have made a working code for your case
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class HoC extends React.Component {
constructor(props) {
super(props);
}
render() {
return <App fromHOC={this.props.fromHOC} field={this.props.field} />;
}
}
const Field = ({ children, name, ...props }) =>
React.createElement(HoC, { field: name, ...props }, children);
export default function App(props) {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div>
I am getting props {props.fromHOC} <div />
<div> The name is {props.field} </div>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Field name="New name" fromHOC="YES!!!" />, rootElement);
Full code here
For your updated question, I have a probable solution; see it it works
import React from "react";
import ReactDOM from "react-dom";
import { Text, Form } from "informed";
const Func = props => {
console.log(props);
return (
<Form id="simple-form">
<label htmlFor="name-field">First name:</label>
{/*(<Text field="name" id="name-field" /> */}
<Text {...props} />
<button type="submit">Submit</button>
</Form>
);
};
const HOC = ({ children, name, ...props }) => {
return React.createElement(Func, { ...props, field: name }, children);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<HOC name="name" id="name-field" />, rootElement);