Hi I am new to React and I just can't wrap my head around some basic things.
One of these things is the following.
Why does the state of itemData change when calling the function with onChange on the textarea element as in this example:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form>
<textarea
onChange={handleitemData}
placeholder="What you have in mind ..."
></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
But not when calling the function with onSubmit on the form element as in this example:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form onSubmit={handleitemData}>
<textarea placeholder="What you have in mind ..."></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
I know the React way is if that does not work try something else but I am trying to actually understand what is going on.
I apreciate your response.
You must prevent the default behavior of the browser on submit:
import React, { useEffect, useState } from "react";
function Createitem() {
const [itemData, setitemData] = useState([]);
const handleitemData = (e) => {
//The line below is necessary
e.preventDefault()
setitemData({
itemTime: Date.now(),
itemText: e.target.value,
});
};
useEffect(() => {
console.log("item", itemData);
}, [itemData]);
return (
<div className="itemBox">
<div>
<form onSubmit={handleitemData}>
<textarea placeholder="What you have in mind ..."></textarea>
<button type="submit">item</button>
</form>
</div>
</div>
);
}
export default Createitem;
attached is a sandbox to see it in action https://codesandbox.io/s/infallible-lederberg-3oj1w?file=/src/App.js:0-630
Related
I'm trying to do this with yup and react-hook-forms
For example, if I'm given an array of ids, then I would like to have a field for each id. The ids are random (i.e., we could have 4 ids or 100 ids). For now, I just want to see if all the input is filled (.required())
This is how I would handle validation without any libraries
export default function App(){
const [ids, setIds] = React.useState(arr1)
const inputValues = React.useRef({});
const handleSubmit = () => {
const { current: values } = inputValues;
console.log(values);
};
const validateInput = event => {
const { name, value } = event.target;
// validation done here
if(true){
inputValues.current[name] = value;
}
};
return (
<div>
<form onSubmit={handleSubmit}>
{ids.map(num => (
<input name={num} onChange={validateInput} required key={num} />
))};
<button type="submit">submit</button>
</form>
</div>
);
}
https://stackblitz.com/edit/react-ts-4jnfx2?file=App.tsx
Now how would I do this with yup and react hook forms to validate the input ?
As per the sandbox below, you can find an implementation with React Hook Form as shown here;
As it's pretty easy to follow from it's own documentation here, you don't need any other variables. So that I made it a bit simplification and only used necessary handleSubmit function, instead of validateInput and references.
import { useState } from "react";
import { useForm } from "react-hook-form";
export function App() {
const { register, handleSubmit } = useForm();
const [data, setData] = useState("");
const [ids] = useState(["1", "3", "40", "18"]);
return (
<form
onSubmit={handleSubmit((data) => {
console.log(data);
setData(JSON.stringify(data));
})}
>
{ids.map((num) => (
<input key={num} name={num} {...register(num, { required: true })} />
))}
<p>{data}</p>
<input type="submit" value="submit" />
</form>
);
}
Please check below code. I don't have experience with TypeScript so I have wrote it in JavaScript
App.js
import React from 'react';
import { useForm } from 'react-hook-form';
import { yupResolver } from '#hookform/resolvers/yup';
import * as yup from 'yup';
const arr1 = [1, 2, 3, 4, 5, 6, 7, 8];
const createSchema = (keys, valueSchema) => yup.object().shape(keys.reduce((acc, key) => ({ ...acc, [key]: valueSchema }), {}));
const mySchema = createSchema(arr1, yup.string().required());
const App = () => {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(mySchema),
});
const onSubmit = (data) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
{arr1.map((item) => (
<div key={item} style={{ border: '1px solid black' }}>
<input {...register(`${item}`)} />
<p>{errors?.[item]?.message}</p>
</div>
))}
<input type="submit" />
</form>
);
};
export default App;
I am new to React and struggle to transform this function component into a class component with a constructor(). I can't figure out how to transform the functions happening onSubmit and onClick.
Thank you very much.
The function component:
import React, { useState, Component } from 'react';
import { render } from 'react-dom';
import './Options.css';
const Test = (props) => {
const [links, setLinks] = useState([]);
const [link, setLink] = useState('');
function handleSubmit(e) {
e.preventDefault();
const newLink = {
id: new Date().getTime(),
text: link,
};
setLinks([...links].concat(newLink));
setLink('');
}
function deleteLink(id) {
const updatedLinks = [...links].filter((link) => link.id !== id);
setLinks(updatedLinks);
}
return (
<div className="OptionsContainer">
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={(e) => setLink(e.target.value)}
value={link}
/>
<button type="submit"> + </button>
</form>
{links.map((link) => (
<div key={link.id}>
<div>{link.text}</div>
<button onClick={() => deleteLink(link.id)}>Remove</button>
</div>
))}
</div>
);
};
export default Test;
When we work with class components a couple of things got changed, we have a new object this.state, a new function this.setState, you have to bind your functions on the constructor if you want to have access to this inside it. I think you'll learn way more if you read the code, so here this is your component as a class component:
import React, { Component } from 'react';
import './Options.css';
class App extends Component {
constructor() {
super()
this.state = {
links: [],
link: '',
}
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const newLink = {
id: new Date().getTime(),
text: this.state.link,
};
this.setState({links: [...this.state.links, newLink], link: ''});
}
deleteLink(id) {
const updatedLinks = [...this.state.links].filter((link) => link.id !== id);
this.setState({...this.state, links: updatedLinks });
}
render() {
console.log(this.state)
return (
<div className="OptionsContainer">
<form onSubmit={this.handleSubmit}>
<input
type="text"
onChange={(e) => this.setState({ ...this.state, link: e.target.value})}
value={this.state.link}
/>
<button type="submit"> + </button>
</form>
{this.state.links.map((link, index) => (
<div key={link.id}>
<span>{link.text}</span>
<button onClick={() => this.deleteLink(link.id)}>Remove</button>
</div>
))}
</div>
);
}
};
export default Test;
Without seeing your original functional component, it's going to be hard to tell you what the class component should look like.
To answer your question about why onSubmit might not be working, it's because onSubmit is expecting to be passed an uncalled function, so that the <form> element can call that function itself when the time is right.
You are currently calling the function this.handleOnSubmit(), which returns nothing, so no handler for the form submit is properly assigned. Try removing the call to the function, like this:
<form onSubmit={this.handleSubmit}>
I have 2 components home and tiny tiny is imported inside home as u can see in the code
I am trying to pass value.toString("html") from tiny.js to home.js
if this is not possible at least help me integrate both tiny and home components as a single object so that I don't have to pass the value as props to a parent component
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: deatils1,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny /> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
tiny.js
import React, { useState, useEffect } from "react";
import dynamic from "next/dynamic";
import PropTypes from "prop-types";
//import the component
const RichTextEditor = dynamic(() => import("react-rte"), { ssr: false });
const MyStatefulEditor = ({ onChange }) => {
const [value, setValue] = useState([]);
console.log(value.toString("html"));
useEffect(() => {
const importModule = async () => {
//import module on the client-side to get `createEmptyValue` instead of a component
const module = await import("react-rte");
console.log(module);
setValue(module.createEmptyValue());
};
importModule();
}, []);
const handleOnChange = (value) => {
setValue(value);
if (onChange) {
onChange(value.toString("html"));
}
};
return <RichTextEditor value={value} onChange={handleOnChange} />;
};
MyStatefulEditor.propTypes = {
onChange: PropTypes.func,
};
export default MyStatefulEditor;
Actually, you already have onChange event in tiny, so you only need to pass another onChange event from home to tiny.
import React from "react";
import Tiny from "./tiny";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [details, setDetails] = useState();
function clickQuestion() {
axios
.post("https://askover.wixten.com/questionpost", {
Name: Questions,
Summary: details,//pass tiny value as summery
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
}
return (
<>
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Tiny onChange={(value) => setDetails(value)}/> //tiny component
</Form>
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
</div>
</div>
</>
);
}
When I submit text in the form, I want to be able to see that text in state via console.log.
I added a console.log to trigger directly after state has been added, but I don't see anything in my console, what am I doing wrong?
What I would like to see: I submit "test1" then I submit "test2", I then want to see in my console "test1, test2" in state.
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useState('');
const addToDoItem = (event) => {
setToDoItem(event.target.value), function(){
console.log(toDoItem)
}
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;
You can log the change in states using a useEffect
I'd suggest making a helper function if you tend to do it often:
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
Which you'd use as follows:
useLog('toDoItem', toDoItem);
I set up an example where the toDoItem is logged as it changes and when you submit, it also logs the change in a todoItems array
const { useState, useEffect } = React;
function useLog(name, property) {
useEffect(() => console.log(name, property), [name, property]);
}
function App() {
return (
<div>
<Field />
</div>
);
}
function Field() {
const [toDoItem, setToDoItem] = useState('');
useLog('toDoItem', toDoItem);
const [todos, setTodos] = useState([]);
useLog('todos', todos);
const changeTodo = (event) => {
setToDoItem(event.target.value);
};
const addTodoItem = (event) => {
event.preventDefault();
setTodos((prev) => prev.concat(toDoItem));
setToDoItem('');
};
return (
<form onSubmit={addTodoItem}>
<input type="text" value={toDoItem} onChange={changeTodo} />
<input type="submit" value="Add" />
</form>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"/>
useState doesn't provide callback after setting value. You can use useEffect instead.
React.useEffect(() => {
console.log(toDoItem);
}, [toDoItem]);
EDIT
It seems like that you want to get toDoItem value on submit. The problem is that the page is reloaded when form is submitted. You can use event.prefentDefault() to stop refreshing on form submission.
<form onSubmit={onSubmit}>
const onSubmit = (event) => {
event.preventDefault()
console.log(toDoItem)
}
import React, { useState } from 'react';
import './App.css';
function App() {
return (
<div>
<Field />
</div>
);
}
function Field(){
const [toDoItem, setToDoItem] = useStateWithCallback('', toDoItem => {
console.log(toDoItem);
});
const addToDoItem = (event) => {
setToDoItem(event.target.value);
}
return (
<form>
<input type="text" value={toDoItem} onChange={addToDoItem}/>
<input type="submit" value="Add" />
</form>
);
}
export default App;
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;