How to trigger a submit function from Custom Component - javascript

I am trying to trigger the form submit function from custom component.
In general my goal is to trigger a boolean state value which is a part of form, and even though the custom component is imported inside the form, somehow it doesn't work.
Problem is about sendEmail function which comes from Custom Component
Here is the codesandbox link and code example below.
Custom Component
import React from "react";
const CustomComponent = (props) => {
return (
<div>
<button onClick={props.sendEmail} type="submit">
send email
</button>
</div>
);
};
export default CustomComponent;
App.js
import { useState } from "react";
import CustomComp from "./CustomComp";
export default function App() {
const [isDone, setIsDone] = useState(false);
const [inputText, setInputText] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
setIsDone(true);
console.log("inputText", inputText);
};
console.log(isDone);
const sendEmail = () => { // this doesn't work
handleSubmit();
console.log("isDone", isDone);
};
const onChangeHandler = (e) => {
setInputText(e.target.value);
};
return (
<form>
<h1>Hello CodeSandbox</h1>
<input type="text" onChange={onChangeHandler} value={inputText} />
<CustomComp sendEmail={sendEmail} />
<button onClick={handleSubmit} type="submit">
submit
</button>
</form>
);
}
Any help will be appreciated

The first problem you have is you're calling handleSubmit in the sendEmail without passing through 'e'
I'd change to this:
const sendEmail = (e) => {
handleSubmit(e);
console.log("isDone", isDone);
};
After doing this, you'll notice your isDone isn't showing what you'd expect. This is because when you're changing the state via handleSubmit, it won't change within this call. You shouldn't worry about logging it as you have. It'll only be an issue if you need to do something with the isDone value

There is no event passed when you trigger it from the custom component so event.preventDefault() will result in an error.
Add the event to sendMail, and pass it to handleSubmit. The onClick will automatically pass the event as first param:
const handleSubmit = (event) => {
event.preventDefault();
setIsDone(true);
console.log("inputText", inputText);
};
const sendEmail = (event) => {
handleSubmit(event);
console.log("isDone", isDone);
};
Updated CodeSandbox

Related

How to navigate programmatically is checkbox true

It is necessary for me at a choice chekboksom that the Page2 component was drawn. I created a useState where I keep track of the value of the checkbox, but I don't know how to navigate programmatically when the checkbox is selected. How can I do this?
export default function Home() {
const [checked, setChecked] = useState(false);
const navigate = useNavigate();
const handleChange = () => {
setChecked(!checked);
};
return (
<>
<input type="checkbox" onChange={handleChange}></input>
</>
);
}
const handleChange = () => {
setChecked(!checked);
if (!checked) {
navigate('/')
}
};
You don't need any state for this, just use the onChange event object. React state updates are asynchronously processed, so trying to enqueue a state update in the handler and navigate based on the updated state won't work.
export default function Home() {
const navigate = useNavigate();
const handleChange = (e) => {
const { checked } = e.target;
if (checked) {
navigate("targetPath");
}
};
return (
<>
<input type="checkbox" onChange={handleChange}></input>
</>
);
}

ReactJS while passing function from parent to child and accessing parent state not returning completely

I was trying to access Parent state when a function is called from Child component, for that created a function in Parent component and passed it to Child, issue is I am not able to access the state completely.
for example on button click I add a new input field and a delete button, suppose I added 10 input fields, and added all of them in state array, but when i click delete button of second input field, the count I get from state is 1, similar if I click 5th delete button i get count as 4 and it only show me 4 items in state, but it has 10 items
Here is an example link https://codesandbox.io/s/add-react-component-onclick-forked-t2i0ll?file=/src/index.js:0-869
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Input = ({ deleteRow, position }) => {
const handleDelete = () => deleteRow(position);
return (
<>
<input placeholder="Your input here" />
<button onClick={handleDelete}>Delete</button>
</>
);
};
const Form = () => {
const [inputList, setInputList] = useState([]);
const onDeleteRow = (position) => {
console.log("inputCount", inputList);
};
const onAddBtnClick = (event) => {
setInputList(
inputList.concat(
<Input
key={inputList.length}
position={inputList.length}
deleteRow={onDeleteRow}
/>
)
);
};
return (
<div>
<button onClick={onAddBtnClick}>Add input</button>
{inputList}
</div>
);
};
ReactDOM.render(<Form />, document.getElementById("form"));
It's called a stale closure.
You can avoid that by not storing react elements inside the state.
Example:
const Form = () => {
const [inputList, setInputList] = useState([]);
const onDeleteRow = (position) => {
console.log("inputCount", inputList);
};
const onAddBtnClick = (event) => {
setInputList([...inputList, inputList.length])
);
};
return (
<div>
<button onClick={onAddBtnClick}>Add input</button>
{inputList.map(item => (
<Input
key={item}
position={item}
deleteRow={onDeleteRow}
/>
)}
</div>
);
};

React custom input passing props to parent functional component while using custom hooks

Am new to react and i have a custom input where am handling the value and input handler via a custom hook but would like to get the value and the input handler to the parent component using the custom input but am stuck on how to achieve this.
I have written the following code.
On the custom hook
import {useReducer} from "react";
const INITAL_STATE = {value:'',valid:false,pristine:true, error:''}
const REDUCER_ACTIONS = { input:"INPUT", blur:"BLUR"}
const reducer = (state,action)=>{
if (action.type === REDUCER_ACTIONS.input){
return {...state, value: action.value}
}
if (action.type === REDUCER_ACTIONS.blur){
return {...state, pristine: false}
}
return INITAL_STATE;
}
const useForm = () => {
const [inputState, dispatch] = useReducer(reducer,INITAL_STATE)
const onBlurHandler = (event) => {
dispatch({type:REDUCER_ACTIONS.blur});
}
const onInputHandler = (event) => {
dispatch({type:REDUCER_ACTIONS.input,value:event.target.value})
}
return {
...inputState,
onBlurHandler,
onInputHandler
}
};
export default useForm;
And for my custom input i have
import useForm from "../../hooks/use-form";
const CustomInput = (props) => {
const {value, onInputHandler, onBlurHandler} = useForm(); //uses custom hook
return <>
<label htmlFor={props.id}>{props.label}</label>
<input value={value} onBlur={onBlurHandler} onInput={onInputHandler}
{...props} />
</>
}
export default CustomInput;
The above custom input has the onInput and onBlur pointing to the custom hooks since i want to reuse the functionality on other input types like select and date pickers without having to duplicate them.
On my parent component am simply calling the Custom input like
function App() {
return (
<div className="container">
<CustomInput onInputHandler={} label="First name"/>
</div>
);
}
export default App;
I would like to pass the onInputHandler and value as a props back to the parent component from the custom input but am stuck on how to do this. How do i proceed?
When you say you need to pass value, I guess you wanted to pass the initial value of the input to CustomInput. To achieve that you can pass another prop.
App.js pass initialValue to CustomInput
<CustomInput
initialValue={"abc"}
label="First name"
/>
In CustomInput pass initialValue prop to useForm hook as an argument.
const { value, onInputHandler, onBlurHandler } = useForm(props.initialValue);
Set the initialValue as the value in initial state in useForm.
const useForm = (initialValue) => {
const [inputState, dispatch] = useReducer(reducer, {
...INITAL_STATE,
value: initialValue
});
...
...
}
To pass the onInputHandler as a prop you can check if onInputHandler is available as a prop and call it along with onInputHandler coming from useForm.
In App.js defines another function that accepts event as an argument.
export default function App() {
const onInputHandler = (e) => {
console.log(e);
};
return (
<div className="App">
<CustomInput
...
onInputHandler={onInputHandler}
label="First name"
/>
</div>
);
}
In CustomInput change the onInput handler like below. You can change the logic as per your needs (I called onInputHandler in useForm and prop).
<input
value={value}
onBlur={onBlurHandler}
onInput={(e) => {
props.onInputHandler && props.onInputHandler(e);
onInputHandler(e);
}}
{...props}
/>
My approach to this will be to simply call the onInputHandler() from hooks and onInputHandler() from the props received from Parent and send the e.target.value as a prop to these functions.
const CustomInput = (props) => {
const { value, onInputHandler, onBlurHandler } = useForm(); //uses custom hook
console.log(value);
const handleInputChange = (e: any) => {
onInputHandler(e);
props.onInputHandler(e.target.value);
};
return (
<>
<label htmlFor={props.id}>{props.label}</label>
<input
value={value}
onBlur={onBlurHandler}
onInput={(e) => {
handleInputChange(e);
}}
{...props}
/>
</>
);
};
export default CustomInput;
And in the parent component we can receive them as props returned from that function and use it according to our requirement.
function App() {
return (
<div className="container">
<CustomInput
label="name"
onInputHandler={(value: string) => console.log("App",value)}
/>
</div>
);
}
export default App;
sandbox link : https://codesandbox.io/s/nifty-lamport-2czb8?file=/src/App.tsx:228-339

Replace parent state with state of child. Button in parent

EDIT: See the comment of O.o for the explanation of the answer and the variant in case you are using classes.
I've come across to something and I can't find the solution.
I have 4 components in my web app:
Parent
child_1
child_2
child_3
I have a button on the Parent, and different forms (with inputs, checkboxes and radiobuttons) at the children.
Each child has his own button that executes several functions, some calculations, and updates the corresponding states. (No states are passed through parent and child).
I need to replace the three buttons of the children with the parent button.
Is there a way that I can execute the functions at the three children from the parent button and retrieve the results? (the results are one state:value per child.)
function Child1(props) {
const [value, setValue] = useState("");
useEffect(() => {
calculate();
}, [props.flag]);
calculate() {
//blah blah
}
onChange(e) {
setValue(e.target.value);
props.onChange(e.target.value); // update the state in the parent component
}
return (
<input value={value} onChange={(e) => onChange(e)} />
);
}
function Parent(props) {
const [flag, setFlag] = useState(false);
const [child1Value, setChild1Value] = useState("");
return (
<div>
<Child1 flag={flag} onChange={(value) => setChild1Value(value)}/>
<button onClick={() => setFlag(!flag)} />
</div>
);
}
I didn't test this but hope this helps you. And lemme know if there is an issue.
Try the following:
create refs using useRef for child form components.
for functional components, in order for the parent to access the child's methods, you need to use forwardRef
using the ref, call child component functions on click of parent submit button (using ref.current.methodName)
See the example code. I have tested it on my local, it is working ok.
Parent
import React, { Fragment, useState, useRef } from "react";
import ChildForm1 from "./ChildForm1";
const Parent = props => {
const [form1Data, setFormData] = useState({});//use your own data structure..
const child1Ref = useRef();
// const child2Ref = useRef(); // for 2nd Child Form...
const submitHandler = e => {
e.preventDefault();
// execute childForm1's function
child1Ref.current.someCalculations();
// execute childForm2's function
// finally do whatever you want with formData
console.log("form submitted");
};
const notifyCalcResult = (calcResult) => {
// update state based on calcResult
console.log('calcResult', calcResult);
};
const handleChildFormChange = data => {
setFormData(prev => ({ ...prev, ...data }));
};
return (
<Fragment>
<h1 className="large text-primary">Parent Child demo</h1>
<div>
<ChildForm1
notifyCalcResult={notifyCalcResult}
ref={child1Ref}
handleChange={handleChildFormChange} />
{/*{do the same for ChildForm2 and so on...}*/}
<button onClick={submitHandler}>Final Submit</button>
</div>
</Fragment>
);
};
export default Parent;
ChildFormComponent
import React, { useState, useEffect, forwardRef, useImperativeHandle } from "react";
const ChildForm1 = ({ handleChange, notifyCalcResult }, ref) => {
const [name, setName] = useState("");
const [calcResult, setCalcResult] = useState([]);
const someCalculations = () => {
let result = ["lot_of_data"];
// major calculations goes here..
// result = doMajorCalc();
setCalcResult(result);
};
useImperativeHandle(ref, () => ({ someCalculations }));
useEffect(() => {
// notifiy parent
notifyCalcResult(calcResult);
}, [calcResult]);
return (
<form className="form">
<div className="form-group">
<input
value={name}// //TODO: handle this...
onChange={() => handleChange(name)}//TODO: notify the value back to parent
type="text"
placeholder="Enter Name"
/>
</div>
</form>
);
};
export default forwardRef(ChildForm1);
Also as a best practice, consider to maintain state and functions in the parent component as much as possible and pass the required values/methods to the child as props.

How to validate form using react hooks?

How to validate form using react hooks ?
I used class components and it worked great, but now decided to use functional components with hooks and don't know the best way to validate form.
My code:
const PhoneConfirmation = ({ onSmsCodeSubmit }) => {
const [smsCode, setSmsCode] = useState('');
const onFormSubmit = (e) => {
e.preventDefault();
if (smsCode.length !== 4) return;
onSmsCodeSubmit(smsCode);
}
const onValueChange = (e) => {
const smsCode = e.target.value;
if (smsCode.length > 4) return;
setSmsCode(smsCode);
};
return (
<form onSubmit={onFormSubmit}>
<input type="text" value={smsCode} onChange={onValueChange} />
<button type="submit">Submit</button>
</form>
)
};
It works but I don't think it's good idea to use handler function inside functional component, because it will be defined every time when the component is called.
Your code is fine, but you can slightly improve it because you don't need a controlled component.
Moreover, you can memorize the component so it won't make unnecessary render on onSmsCodeSubmit change due to its parent render.
const FORM_DATA = {
SMS: 'SMS'
}
const PhoneConfirmation = ({ onSmsCodeSubmit, ...props }) => {
const onSubmit = e => {
e.preventDefault();
const data = new FormData(e.target);
const currSmsCode = data.get(FORM_DATA.SMS);
onSmsCodeSubmit(currSmsCode);
};
return (
<form onSubmit={onSubmit} {...props}>
<input type="text" name={FORM_DATA.SMS} />
<button type="submit">Submit</button>
</form>
);
};
// Shallow comparison by default
export default React.memo(PhoneConfirmation)

Categories