How to do state operations in function in react without class - javascript

this is my React worksection.js file and its function made instead of class
export default function WorkSection() {
now i need to do here constructor to initialise state and do function operations which i'll call on button click
return (
<div className={classes.section}>
<GridContainer justify="center">
<GridItem cs={12} sm={12} md={8}>
<h2 className={classes.title}>Work with us</h2>
<h4 className={classes.description}>
BAIOS BAY LLP is looking for collaborations with you, Contact us
today !
</h4>
<form onSubmit={this.handleSubmit}>
<GridContainer>
<GridItem xs={12} sm={12} md={6}>
<CustomInput
labelText="Your Name"
id="name"
onChange={this.handleChange}
defaultValue={this.state.name}
formControlProps={{
fullWidth: true
}}
/> </GridItem> </GridContainer>
</div>
);
}
this is my form where i am submitting name and will add button click so how can i initialise state and functions to call onclick functions where
my functions are as :
constructor(props) {
super(props);
this.state = {
name : ''}
}
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
handleClick = event => {
this.setState({
[event.target.id]: event.target.checked
});
}
handleSubmit = event => {
event.preventDefault();
if (this.state.username === '') {
this.showMessage('Info', "Username is empty");
return;
}
}
i need to place this function and i did it with class worksection but how to do it with export default function Worksection()

The thing you're probably looking for called react hooks. They allow you to use state management in your functional components. They're cool because they're lightweight in compare with class components.
First, import useState function from react:
import { useState } from 'react'
Then, before your return, add these lines:
const [name, setName] = useState('');
The first argument here is the name of your state property, and the second one is the function to change it.
So, instead of this:
handleChange = event => {
this.setState({
[event.target.id]: event.target.value
});
}
Write this:
handleChange = event => {
setName(event.target.value);
}
If you want to make it more complex, you can rewrite your hook from this:
const [name, setName] = useState('');
to this:
const [state, setState] = useState({
name: '',
checked: false,
});

export default function WorkSection() {
const { register, handleSubmit } = useForm();
const onSubmit = data => {
axios
.get("
...................... my code ...............
and my input form is :
return (
<div className={classes.section}>
<form onSubmit={handleSubmit(onSubmit)}>
<Input
name="Name"
placeholder="Name"
inputRef={register}
fullWidth={true}
/>
<Input
name="email"
type="email"
placeholder="Email"
fullWidth={true}
inputRef={register({ required: true })}
/>
<Input
name="contact"
placeholder="Contact"
fullWidth={true}
inputRef={register({ required: true })}
/>
<Input
name="description"
placeholder="Message"
multiline={true}
fullWidth={true}
inputRef={register({ required: true })}
/>
<button className="btnColor" justify="center" type="submit">
Send Message
</button>
</GridItem>
</GridContainer>
</form>
</div>
Basically i used
inputRef={register}
and other part as satated above.
Right code , worked for me ~

Related

Not able to write into textfield

i'm quite new with react and i'm building a form with react-hook and useState to manage my datas after the submit.
I'm not able to use textfield as they are blocked. I think that i make some errors into value/onChange parameters but i don't know what type of error.
import React, { useState } from "react";
import {
TextField,
MenuItem,
Typography,
Checkbox,
Divider,
Button,
} from "#mui/material";
import { MdError } from "react-icons/md";
import { BsArrowRight } from "react-icons/bs";
import "../style/contactform.scss";
import { useForm } from "react-hook-form";
const initialState = {
name: "",
email: "",
};
const ContactForm = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
const [state, setState] = useState(initialState);
const { name, email } = state;
const handleInputChange = (e) => {
const { name, value } = e.target;
setState({ ...state, [name]: value });
};
const onSubmit = (e) => {
e.preventDefault();
console.log("form submit");
setState(initialState);
};
return (
<form className="contact-form" onSubmit={handleSubmit(onSubmit)}>
<Typography variant="h4" className="form-title">
Be the first.
</Typography>
<div className="form-component">
<TextField
id="standard-basic"
label="Nome*"
variant="standard"
name="nome"
value={name}
onChange={handleInputChange}
{...register("nome", {
required: true,
})}
/>
{errors?.nome?.type === "required" && (
<MdError className="form-validation-icon" />
)}
</div>
<Divider className="form-hr" />
<div className="form-component">
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
value={email}
onChange={handleInputChange}
{...register("email", {
required: true,
pattern: {
value:
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
},
})}
/>
{errors?.email?.type === "required" && (
<MdError className="form-validation-icon" />
)}
{errors?.email?.type === "pattern" && (
<Typography variant="p" className="form-validation-email">
Inserisci un indirizzo email valido.
</Typography>
)}
</div>
<Divider className="form-hr" />
<Button className="form-submit" type="submit" variant="contained">
<BsArrowRight />
</Button>
</form>
);
};
export default ContactForm;
Textfields are completely block but initial state is actually working, do i miss something?
Can you help me?
To assign initial values using the useForm hook, you pass it under the defaultValues parameter passed to the hook like so:
const {
register,
handleSubmit,
reset
formState: { errors },
} = useForm({
defaultValues: initialState
});
Then just pass the ...register name and email to the inputs. There is no need to assign values to them again:
<TextField
id="standard-basic"
label="Name*"
variant="standard"
name="name"
{...register("name", {
required: true,
})}
/>
// for the email..
<TextField
id="standard-basic"
label="Email*"
variant="standard"
name="email"
{...register("email", {
required: true,
pattern: {
value: /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+#[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,},
})}
/>
If you'll notice, the values are off the text fields already and there's also no need for the handleInputChange function. The useForm hook takes care of that.
Edit:
In addition to the onSubmit function, the handleSubmit from useForm passes a data object into the function like this:
const onSubmit = (data) => {
console.log("form submitted", data);
reset(); // this can be destructured of the `useForm` hook.
};
For more info check their documentation

Mock function not being called using jest and testing library on react app

I am using jest and testing library to test a form. But the mock function is not being called. And the last line expect(mockOnSubmit).toHaveBeenCalled(); always fails.
Here's my test code:
App.test.js
import { render, fireEvent, cleanup } from "#testing-library/react";
import App from "./App";
import { act } from "react-dom/test-utils";
afterEach(cleanup);
test("email and password field are clear for submit", async () => {
const mockOnSubmit = jest.fn();
const { getByPlaceholderText, getByText } = render(
<App onSubmit={mockOnSubmit} />
);
const emailNode = getByPlaceholderText(/email/i);
const passwordNode = getByPlaceholderText(/password/i);
const testEmail = "test#example.com";
const testPassword = "123123";
await act(async () => {
fireEvent.change(emailNode, {
target: { value: testEmail }
});
fireEvent.change(passwordNode, {
target: { value: testPassword }
});
});
expect(emailNode.value).toBe(testEmail);
expect(passwordNode.value).toBe(testPassword);
const submit = getByText(/submit/i);
await act(async () => {
fireEvent.click(submit);
});
expect(mockOnSubmit).toHaveBeenCalled();
});
App.js
import { useForm } from "react-hook-form";
import { Controller } from "react-hook-form";
export default function App({ onSubmit = (data) => console.log(data) }) {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="email"
control={control}
defaultValue=""
rules={{
required: `Email is required.`,
pattern: {
value: /^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i,
message: "Invalid email address"
}
}}
render={({ field }) => {
return (
<input
placeholder="Email"
type="text"
key="email"
name="email"
id="email"
{...field}
/>
);
}}
/>
<Controller
name="email"
control={control}
defaultValue=""
rules={{
required: `Password is required.`
}}
render={({ field }) => {
return (
<input
placeholder="Password"
type="text"
key="password"
name="password"
id="password"
{...field}
/>
);
}}
/>
<input type="submit" value="submit" />
</form>
);
}
This minimal implementation is also available at
https://codesandbox.io/s/react-hook-form-testing-olo4i
Sidenote: This works fine without react-hook-form implementation. But as I add handleSubmit from react-hook-form it does not work anymore. Here's a working version by #Florian Motteau where it is working by bypassing react-hook-form
You are checking that the onSubmit props has been called, but in your component your are not calling this props when the form is submitted. So the mock you provide in the test will never get called. Besides this, in App.js, the onSubmit props is passed an evaluation of handleSubmit (handleSubmit()), instead you must pass a reference to the handler function (handleSubmit, or () => handleSubmit()).
Solving these two problems you get :
// App.js
export default function App({ onSubmit = (data) => console.log(data) }) {
const { control, handleSubmit } = useForm();
return (
<form onSubmit={onSubmit}>
<Box sx={{ mt: 2 }}>
<EmailField label="Email" name="email" control={control} />
</Box>
<Box sx={{ mt: 2 }}>
<PasswordField label="Password" name="password" control={control} />
</Box>
<Box sx={{ mt: 2 }}>
<Button name="submit" type="submit" variant="contained">
Submit
</Button>
</Box>
</form>
);
}
I also suggets that you use visual text to interact with DOM elements :
fireEvent.click(getByText("Submit"));
Fixed sandbox : https://codesandbox.io/s/react-hook-form-testing-forked-vs9ez?file=/src/App.js
Finally, you should not have to wrap things in act(), react-testing-library will do this for you, see https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning.

React Js, onChange {setState} event not working

I'm working my practice app using ReactJs and Django. My problem is when I update the Data via Axios "onChange" event not working.
updateForm.js
class Contact extends Component {
constructor(props) {
super(props);
this.state = {
Name: '',
Contact: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleformsubmit = this.handleformsubmit.bind(this);
}
handleformsubmit = (event, requestType, id) => {
const Name = event.target.elements.Name.value;
const Contact = event.target.elements.Contact.value;
switch (requestType) {
case 'put':
return axios.put(`http://127.0.0.1:8000/Contact/${id}/`, {
Name: Name,
Contact: Contact
})
.then(res => console.log(res))
.catch(error => console.log(error));
}
}
handleChange(event) {
this.setState({ Name: event.target.value });
this.setState({ Contact: event.target.value });
}
render() {
const Name = this.state.Item_no;
const Contact = this.state.Supplier;
return (
<Form onSubmit={(event) => this.handleformsubmit(
event,
this.props.requestType,
this.props.id)}>
<div className="container">
<GridContainer component="span">
<Card>
<CardHeader color="primary">
<h4>Update Contact Info</h4>
</CardHeader>
<CardBody>
<GridContainer>
<GridItem sm={12} md={6}>
<CustomInput
labelText="Name"
type="text"
id="Name"
name="Name"
formControlProps={{
fullWidth: true
}}
value={Name}
onChange={this.handleChange}
/>
</GridItem>
<GridItem sm={12} md={6}>
<CustomInput
labelText="Contact"
name="Contact"
type="text"
id="Contact"
formControlProps={{
fullWidth: true
}}
value={Contact}
onChange={this.handleChange}
/>
</GridItem>
I already done searching and tried the possible solution's but nothing works. Nothing wrong with "put" actions but on my onChange event not trigger when I update the data. Help me how to work this.Thank you!
From your code, I can see The onChange function just update the new state and the function handleformsubmit will only be called until you submit the form
It looks like your input value is controlled, which refuses any changes from handleChange
Try value={this.state.Name}

Arrow Function React Component different from "equivalent" counterpart

I'm following a tutorial and I am getting confused with my arrow version of it and their function version.
I have a LoaderButton.js and I can write the component as a normal functional component or an arrow component:
Functional Component:
export default function LoaderButton({
isLoading,
className = "",
disabled = false,
...props
}) {
return (
<Button
className={`LoaderButton ${className}`}
disabled={disabled || isLoading}
{...props}
>
{isLoading && <Glyphicon glyph="refresh" className="spinning" />}
{props.children}
</Button>
);
}
Arrow Component:
const LoaderButton = (
isLoading,
className = "",
disabled = false,
...props ) => (
<Button
className={`LoaderButton ${className}`}
disabled={disabled || isLoading}
{...props}
>
{isLoading && <Glyphicon glyph="refresh" className="spinning" />}
{props.children}
</Button>
)
export default LoaderButton
And the LoaderButton is imported and used here in my Login.js:
export default function Login() {
const history = useHistory();
const { userHasAuthenticated } = useAppContext();
const [isLoading, setIsLoading] = useState(false);
const [fields, handleFieldChange] = useFormFields({
email: "",
password: ""
});
function validateForm() {
return fields.email.length > 0 && fields.password.length > 0;
}
async function handleSubmit(event) {
event.preventDefault();
setIsLoading(true);
try {
await Auth.signIn(fields.email, fields.password);
userHasAuthenticated(true);
history.push("/");
} catch (e) {
onError(e);
setIsLoading(false);
}
}
return (
<div className="Login">
<form onSubmit={handleSubmit}>
<FormGroup controlId="email" bsSize="large">
<ControlLabel>Email</ControlLabel>
<FormControl
autoFocus
type="email"
value={fields.email}
onChange={handleFieldChange}
/>
</FormGroup>
<FormGroup controlId="password" bsSize="large">
<ControlLabel>Password</ControlLabel>
<FormControl
type="password"
value={fields.password}
onChange={handleFieldChange}
/>
</FormGroup>
<LoaderButton
block
type="submit"
bsSize="large"
isLoading={isLoading}
disabled={!validateForm()}
>
Login
</LoaderButton>
</form>
</div>
);
}
The standard functional component works as expected.
But the arrow function component seems to have isLoading stuck to true
AND gets this error:
Warning: Failed prop type: Invalid prop `disabled` of type `object` supplied to `Button`, expected `boolean`.
I thought arrow function components were supposed to be a simpler way to write function components.
I keep thinking it has to do with binding and therefore it's somehow binding the props I have different but I can't find any information on the differences of their bindings. I thought if my Login.js is binding according to the way it's written then I should be fine?
I honestly would prefer to write using an arrow function syntax.
They aren't quite equivalent. You didn't destructure props correctly. Wrap all the props with {} so your functional component is taking a single props argument.
const LoaderButton = ({
isLoading,
className = "",
disabled = false,
...props
}) => (
<Button
className={`LoaderButton ${className}`}
disabled={disabled || isLoading}
{...props}
>
{isLoading && <Glyphicon glyph="refresh" className="spinning" />}
{props.children}
</Button>
);
export default LoaderButton

Storing a value and duplicating a field onClick

I have a field where a user can input there email address, however i also have a button underneath that field that says 'Add more', I am struggling to find a way to store the email value and have the user input another email, I am using React hooks for this process, for instance I understand that i would need to store the value of the email inside a
const [email, setEmail] = useState=();
below is my code relating to the problem
<Grid item xs={12} sm={6}>
<TextField
className={classes.field}
id="contactEmails"
name="contactEmails"
label="Contact Email(s)"
fullWidth
autoComplete="lname"
inputProps={{
maxLength: 250
}}
/>
</Grid>
<Grid item xs={12} sm={6}>
<Button className={classes.addButton} variant='contained' color='primary'> Add Another Email</Button>
</Grid>
You don't want to have one stateful email but rather emails (an array of emails):
const [emails, setEmails] = useState([]);
To then add an email, take the previous emails and add one:
setEmails([...emails, "new.email#example.com"]);
Replacing an email works the same way:
setEmails(emails.map(prev => prev === "previous#example.com" ? "new#example.com" : prev));
Now you have to render all those emails too, for that you can map the array of emails into an array of components and return that from your component:
return emails.map(email => <p> {email} </p>);
You might want to have a look at useReducer for this instead though, that'll be far more elegant...
Here you have another option already working: https://jsfiddle.net/hpuey7zn/
Explanation:
In the state you store the current input value (see function handleChange) and an array of emails
onClick button adds the current email to the array if it is not included (see handleClick).
class App extends React.Component {
state = {
input: '',
emails: []
}
handleChange = e => {
this.setState({ input: e.target.value });
}
handleClick = ev => {
ev.preventDefault();
const { emails, input } = this.state;
if (!emails.includes(input))
this.setState({ emails: [...emails, input]});
}
render() {
return (
<div>
<input
name="email" label="email" type="email"
onChange={ this.handleChange }
/>
<button onClick={ev => { this.handleClick(ev)}}>
Add Another Email
</button>
<div>EMAILS:</div>
<ul>{this.state.emails.map(email => <li>{email}</li>)}</ul>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById("root"))
And here you have a version with hooks:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [input, setInput] = useState('');
const [emails, setEmails] = useState([]);
return (
<div>
<input name="email" label="email" type="email"
onChange={ ev => {setInput(ev.target.value)} }
/>
<button onClick={ev => {
if (!emails.includes(input)) setEmails([...emails, input])
}}>
Add Another Email
</button>
<div>EMAILS:</div>
<ul>{emails.map(e => <li>{e}</li>)}</ul>
</div>
)
}
const rootElement = document.getElementById("root");
if (rootElement) ReactDOM.render(<App />, rootElement);

Categories

Resources