How to call child submit method from another child component - reactjs

Im trying to solve this for 2 days but cant.
How to call onSubmit method in CreateProject component when onApply function called in ModalContent component with Typescript and react-hook-form.
The idea: when I clicked button in ModalContent it should call react-hook-form onSumbit method in CreateProject. Then CreateProject calls onSubmit in parent component of these two childs.
Or maybe the idea of such component structure is wrong?
Thanks everyone for any answers.
Parent component:
import { ModalBody } from '../../components/modals'
import useCreateProject from '../../api/hooks/createProject'
export default function New(): JSX.Element {
const [modalVisible, setModalVisible] = React.useState(true)
const onSubmit = useCreateProject((data) => {
const { createProject } = data.data.data
console.log(data)
})
const onApply = () => {
console.log(123)
}
return (
<ModalContent
name={'Create project'}
visible={modalVisible}
onApply={onApply}
onCancel={() => setModalVisible(false)}
>
<ModalBody>
<CreateProject onSubmit={onSubmit.mutate} />
</ModalBody>
</ModalContent>
)
}
Child component
import React from 'react'
import Input from '../Inputs/Input'
import Textarea from '../Inputs/Textarea'
import FileUploader from '../uploader/FileUploader'
import Tasks from '../forms/Tasks'
import { useForm } from 'react-hook-form'
import {
draftBudgetMaxLength,
projectNameMaxLength,
descriptionMaxLength,
} from '../../constants/createProjectModal'
import ButtonSecondary from '../buttons/ButtonSecondary'
export interface IModalInputs {
create_project_name: string
}
export interface ICreateProjectProps {
onSubmit: (values: IModalInputs) => void
}
const СreateProject: React.FC<ICreateProjectProps> = ({
onSubmit,
}): React.ReactElement => {
const {
register,
getValues,
control,
handleSubmit,
formState: { errors },
} = useForm<IModalInputs>()
return (
<>
<form onSubmit={handleSubmit(() => onSubmit(getValues()))}>
<div className="p-4">
<div className="flex flex-col -m-1.5">
<div className="m-1.5">
<Input
type="text"
label="Project name"
name="create_project_name"
register={register}
error={errors.create_project_name}
options={{
required: true,
maxLength: projectNameMaxLength,
}}
/>
</div>
</div>
</div>
</form>
</>
)
}
export default СreateProject
Child component with click event
import React from 'react'
import Image from 'next/image'
import close from '../../assets/close.svg'
import ButtonSecondary from '../buttons/ButtonSecondary'
interface IModalContentProps {
children: React.ReactElement
onApply?: () => void
visible: boolean
buttonName?: string
}
const ModalContent: React.FC<IModalContentProps> = ({
name,
children,
visible,
onCancel,
onApply,
buttonName,
}) => {
return (
<>
{visible && (
<div className="flex p-4 space-x-2 justify-end">
{children}
<ButtonSecondary
click={onApply}
type={'submit'}
label={buttonName || 'Ok'}
id={buttonName}
shown={true}
styleClass="styleClass"
paddingClass="py-2 py-2 pr-4 pl-2"
/>
</div>
)}
</>
)
}
export default ModalContent

The problem was resolved with useRef and useImperativeHandle hooks.
Parent:
export default function New(): JSX.Element {
const [modalVisible, setModalVisible] = React.useState(true)
const childRef = React.useRef<any>()
const onSubmit = useCreateProject((data) => {
const { createProject } = data.data.data
console.log(data)
})
return (
<ModalContent
name={'Create project'}
visible={modalVisible}
onApply={() => childRef.current.SubmitForm()}
onCancel={() => setModalVisible(false)}
>
<ModalBody>
<CreateProject onSubmit={onSubmit.mutate} ref={childRef} />
</ModalBody>
</ModalContent>
)
}
Child:
export interface IModalInputs {
create_project_name: string
}
export interface ICreateProjectProps {
onSubmit: (values: IModalInputs) => void
}
function CreateProject(props: ICreateProjectProps, ref) {
const {
register,
getValues,
control,
handleSubmit,
formState: { errors },
} = useForm<IModalInputs>()
useImperativeHandle(ref, () => ({
SubmitForm() {
handleSubmit(() => props.onSubmit(getValues()))()
},
}))
return (
<>
<form ref={ref}>
<div className="p-4">
<div className="flex flex-col -m-1.5">
<div className="m-1.5">
<Input
type="text"
label="Project name"
name="create_project_name"
register={register}
error={errors.create_project_name}
options={{
required: true,
maxLength: projectNameMaxLength,
}}
/>
</div>
</div>
</div>
</form>
</>
)
}
export default React.forwardRef(CreateProject)

Related

Formik QRcodeReader button submit for itself

I created a custom QRcode reader that added as button to formik customTextInout.tsx but when I click on it, it will submit with no value in it.
in this code I created a button that when you click on it, it will show the QrCodeReader.
QrReaderButton.tsx
import { useState } from "react";
import { QrReader } from "react-qr-reader";
import qrcode from "../assets/qr-code.png";
interface Props {
setQrData: any;
}
const QrReaderButton: React.FC<Props> = (props) => {
const [data, setData] = useState<any>();
const [show, setShow] = useState<boolean>(false);
let a;
return (
<div className="flex flex-col w-max items-center">
<button
type="button"
onClick={() => (!show ? setShow(true) : setShow(false))}
>
<img src={qrcode} alt="qr-code" width={40} />
</button>
{show && (
<div className="w-96">
<QrReader
onResult={(result: any, error) => {
if (!!result) {
setData(JSON.parse(result?.text));
props.setQrData(JSON.parse(result?.text));
}
if (!!error) {
// console.info(error);
}
}}
constraints={{ facingMode: "user" }}
/>
<p> {data ? JSON.stringify(data) : "No result"}</p>
</div>
)}
</div>
);
};
export default QrReaderButton;
in this code I created a custom input for formik with help of formik official docs
CustomTextInput.tsx
import {
FormControl,
FormHelperText,
FormLabel,
Input,
} from "#chakra-ui/react";
import QrReaderButton from "../QrReaderButton";
const CustomTextInput = ({
field,
form: { touched, errors },
...props
}: any) => {
return (
<FormControl isInvalid={errors.name && touched.name}>
<FormLabel>{props.label}</FormLabel>
<div className="flex gap-2">
<Input {...field} {...props} />
<div className="w-max">
<QrReaderButton
setQrData={() => {
console.log("test");
}}
/>
</div>
</div>
<FormHelperText>this is helper</FormHelperText>
</FormControl>
);
};
export default CustomTextInput;
in this code I used CustomTextInout component with chakra-ui modal.
FormModal.tsx
import {
useDisclosure,
Button,
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
FormLabel,
useToast,
ModalFooter,
} from "#chakra-ui/react";
import { Formik, Form, Field } from "formik";
import { useNavigate } from "react-router-dom";
import CustomNumberInput from "./CustomInputs/CustomNumberInput";
import CustomOptionInput from "./CustomInputs/CustomOptionInput";
import CustomTextInput from "./CustomInputs/CustomTextInput";
import TokenButton from "./TokenButton";
interface Props {
key: string;
Title: string;
buttonValue?: string;
image: string;
nav: string;
initialValues: any;
isLoading: boolean;
apiHook: any;
validateName: any;
}
const FormModal: React.FC<Props> = (props) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const navigate = useNavigate();
const toast = useToast();
return (
<div className="w-full content-center ">
<TokenButton
nav={props.nav}
image={props.image}
title={props.Title}
onClick={onOpen}
/>
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>{props.Title}</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Formik
initialValues={props.initialValues}
onSubmit={(values, actions) => {
setTimeout(() => {
alert(JSON.stringify(values, null, 2));
actions.setSubmitting(false);
}, 1000);
props
.apiHook(values)
.unwrap()
.catch((e: any) => {
// alert(JSON.stringify(e, null, 2))
toast({
status: "error",
title: "Error",
description: JSON.stringify(e, null, 2),
isClosable: true,
});
if (e.status == 401) {
setTimeout(() => {
navigate("/login");
}, 1000);
localStorage.clear();
}
});
}}
>
{(propsForm) => (
<Form
onSubmit={propsForm.handleSubmit}
onReset={propsForm.handleReset}
className="w-full h-full bg-[#ced8d7ac] shadow-2xl rounded-2xl p-4 items-center content-center text-current"
>
<FormLabel>{props.Title}</FormLabel>
<div className="grid lg:grid-cols-1 md:grid-cols-2 gap-4">
{Object.keys(props.initialValues).map((key) => {
return (
<Field
id={key}
key={key}
name={key}
label={key}
backgroundColor="#ffffffe1"
component={CustomTextInput}
/>
);
})}
</div>
<Button
type="submit"
isLoading={props.isLoading}
className="gre mt-2"
>
{props.Title}
</Button>
</Form>
)}
</Formik>
</ModalBody>
<ModalFooter>
<Button colorScheme="blue" mr={3} onClick={onClose}>
Close
</Button>
<Button variant="ghost">{props.buttonValue}</Button>
</ModalFooter>
</ModalContent>
</Modal>
</div>
);
};
export default FormModal;
Default behavior of button in form is submit. so you need to make sure that you specified a type except the submit.
See reference in here
Your code will be like below
QrReaderButton
import { useState } from "react";
import { QrReader } from "react-qr-reader";
import qrcode from "../assets/qr-code.png";
interface Props {
setQrData: any;
}
const QrReaderButton: React.FC<Props> = (props) => {
const [data, setData] = useState<any>();
const [show, setShow] = useState<boolean>(false);
let a;
return (
<div className="flex flex-col w-max items-center">
<button
type="button" //make sure you add this
onClick={() => (!show ? setShow(true) : setShow(false))}
>
<img src={qrcode} alt="qr-code" width={40} />
</button>
{show && (
<div className="w-96">
<QrReader
onResult={(result: any, error) => {
if (!!result) {
setData(JSON.parse(result?.text));
props.setQrData(JSON.parse(result?.text));
}
if (!!error) {
// console.info(error);
}
}}
constraints={{ facingMode: "user" }}
/>
<p> {data ? JSON.stringify(data) : "No result"}</p>
</div>
)}
</div>
);
};
export default QrReaderButton;

How can I display the result of the child component in the parent component?

my application consists of a filter select by provinces, the filter is a modal that shows me the result in the filter component (child), I would like to show the result in the parent not in the filter modal. I do not know what to pass to the parent to show the result or how to show it on the screen.
///parent component
import React, { useState, useEffect } from 'react'
import { useDispatch } from "react-redux";
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import { selectClinics } from '../../actions/detailClinics'
import { useNavigate } from "react-router-dom";
import contentUtils from '../../lib/contentUtils'
import { SearchFilterClinics } from './SearchFilterClinics'
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
const ClinicList = () => {
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFiltered, setClinicListFiltered] = useState<Clinic[]>([]);
const [searchClinic, setSearchClinic] = useState("");
const dispatch = useDispatch();
const navigate = useNavigate();
///modal control
const [isOpen, setIsOpen] = useState(false);
const openModal = () => setIsOpen(true);
const closeModal = () => setIsOpen(false);
console.log(searchClinic);
const fetchClinicList = async () => {
getClinic().then((response)=>{
console.log(response)
setClinicList(response);
setClinicListFiltered(response)
}).catch ( (error) => {
console.error(error);
throw error;
});
}
const handleChange=e=>{
setSearchClinic(e.target.value);
filter(e.target.value);
}
const filter=(termSearch)=>{
const resultSearch= clinicList.filter((element)=>{
if(element.title.toString().toLowerCase().includes(termSearch.toLowerCase())
){
return element;
}
});
setClinicListFiltered(resultSearch);
}
function handleAddToDetail(clinic) {
dispatch(selectClinics(clinic));
navigate('clinicdetail');
}
function goToPageSearchFilterClinics() {
navigate('filterclinics');
}
useEffect (() => {
fetchClinicList();
}, []);
return(
<>
<div style={{display: 'flex'}}>
<div style={{width:'5rem'}}>
{/* <button onClick={() => goToPageSearchFilterClinics()}>filtro</button> */}
<button onClick={openModal}>filtro</button>
</div>
< SearchFilterClinics isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}></SearchFilterClinics>
<Form className="d-flex">
<Form.Control
type="search"
value={searchClinic}
placeholder="Search"
className="me-2"
aria-label="Search"
onChange={handleChange}
/>
</Form>
</div>
<div className="content-cliniclist">
{
clinicListFiltered.map((clinic) => (
<div style={{marginBottom: '3rem'}}>
<button
type="button"
onClick={() => handleAddToDetail(clinic)}
style={{all: 'unset'}}
>
<div>
{/* <img src={ contentUtils.getLargeImageUrl(clinic.logoWidth )} alt="#"></img> */}
<div>{clinic.title}</div>
<div>{clinic.propsPhone}</div>
<div>{clinic.mobile}</div>
<div>{clinic.email}</div>
<div>{clinic.registry}</div>
</div>
</button>
</div>
))
}
</div>
</>
)
}
export default ClinicList;
////child component
import React, { useState, useEffect } from 'react'
import Select, { SingleValue } from 'react-select'
import { getClinic } from '../../api/drupalAPI'
import {Clinic} from '#icofcv/common';
import "./Modal.css";
interface Props {
isOpen: boolean,
clinicFilter: String,
closeModal: () => void
}
export const SearchFilterClinics : React.FC<Props> = ({ children, isOpen, closeModal, clinicFilter }) => {
////filter
type OptionType = {
value: string;
label: string;
};
const provincesList: OptionType[] = [
{ value: 'Todos', label: 'Todos' },
{ value: 'Valencia', label: 'Valencia' },
{ value: 'Castellon', label: 'Castellon' },
{ value: 'Alicante', label: 'Alicante' },
]
const [clinicList, setClinicList] = useState<Clinic[]>([]);
const [clinicListFilteredSelect, setClinicListFilteredSelect] = useState<Clinic[]>([]);
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
const handleChangeSelect = async (provinceList: SingleValue<OptionType>) => {
getClinic().then((response) => {
setClinicList(response);
setClinicListFilteredSelect(response)
setFilterSelectClinic(provinceList);
filterSelect(provinceList );
}).catch ((error) => {
console.error(error);
throw error;
});
}
const filterSelect=(termSearch)=>{
const resultFilterSelect = clinicList.filter((element) => {
if(element.province?.toString().toLowerCase().includes(termSearch.value.toLowerCase() )
){
return element;
}
});
setClinicListFilteredSelect(resultFilterSelect);
}
const handleModalContainerClick = (e) => e.stopPropagation();
return (
<>
<div className={`modal ${isOpen && "is-open"}`} onClick={closeModal}>
<div className="modal-container" onClick={handleModalContainerClick}>
<button className="modal-close" onClick={closeModal}>x</button>
{children}
<div>
<h1>Encuentra tu clínica</h1>
</div>
<div>
<form>
<label>Provincia</label>
<Select
defaultValue={filterSelectClinic}
options={provincesList}
onChange={handleChangeSelect}
/>
</form>
{
clinicListFilteredSelect.map((clinicFilter) => (
<div>
<div>{clinicFilter.title}</div>
<div>{clinicFilter.propsPhone}</div>
<div>{clinicFilter.mobile}</div>
<div>{clinicFilter.email}</div>
<div>{clinicFilter.province} </div>
<div>{clinicFilter.registry}</div>
</div>
))
}
</div>
</div>
</div>
</>
)
}
You need to pass a callback function to the parent and set the state value in the parent, not in the child. (This is known as lifting state up.)
In your case this would involve moving
const [filterSelectClinic, setFilterSelectClinic] = useState<SingleValue<OptionType>>(provincesList[0]);
Into the parent component. Then passing the setFilterSelectClinic function into the child component.
<SearchFilterClinics setFilterSelectClinic={(value) => setFilterSelectClinic(value)} isOpen={isOpen} closeModal={closeModal} clinicFilter={clinicFilter}/>
The value (within the parenthesis) is being passed up by the child component. This is the value you set here:
getClinic().then((response) => {
...
// this is the function we pass in from the parent. It set's the value
// of the callback function to provinceList
setFilterSelectClinic(provinceList);
...
We then setFilterSelectClinic to that value. Meaning filterSelectClinic now has the value passed up in the callback.

passing a variable from child component to parent component in Next.js

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>
</>
);
}

How can I use forwardRef in React Component?

Can someone help me? I'm creating a component inside React, and I want to make it more accessible using forwardRef. In my case, I'm making a button and I'm using the button's properties, and a few more I've done to make it more dynamic.
This is a summary of my code.
export interface ButtonProps
extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children?: React.ReactNode;
loading?: boolean;
}
class Button extends React.Component<ButtonProps> {
render() {
const {
...otherProps
} = this.props;
return (
<button {...(otherProps)}></button>
)
}
}
export default Button;
I tried to start something, but right away it gave an error
const ForwardedElement = React.forwardRef<ButtonProps, HTMLButtonElement> (
(props: ButtonProps, ref) => <Button {...props}/>
)
export default ForwardedElement;
I suggest you to use useImperativeHandle hook
useImperativeHandle customizes the instance value that is exposed to parent components when using ref. Let's visualize that with an example.
Here's a component as search bar
import React, {forwardRef, useImperativeHandle, useRef} from "react";
import {Form} from "reactstrap";
const SearchBar = (props, ref) => {
const buttonRef = useRef<any>();
useImperativeHandle(ref, () => ({
getCurrentValue: () => {
return buttonRef.current ? buttonRef.current["value"] : '';
},
setCurrentValue: (value) => {
if (buttonRef.current) {
buttonRef.current["value"] = value;
}
}
}));
return (
<Form className="p-3 w-100" onSubmit={(e) => props.onSubmitHandler(e)}>
<div className="form-group m-0">
<div className="input-group">
<input
type="text"
className="form-control"
placeholder="Search ..."
aria-label="Word to be searched"
ref={buttonRef}
/>
<div className="input-group-append">
<button className="btn btn-primary" type="submit">
<i className="mdi mdi-magnify" />
</button>
</div>
</div>
</div>
</Form>
);
}
export default forwardRef(SearchBar);
This is the header component in which we call our search bar component
import React, {useEffect, useRef, useState} from 'react';
import SearchBar from '../Form/Search/SearchBar';
import Router from 'next/router';
const Header = () => {
const mobileSearchRef = useRef<any>();
const [search, setSearch] = useState<any>(false);
const codeSearchHandler = (e) => {
e.preventDefault();
setSearch(!search);
if (mobileSearchRef.current) {
if (mobileSearchRef.current.getCurrentValue() == '') {
return;
}
}
Router.push({
pathname: '/search',
query: {
searchTerm: mobileSearchRef.current
? mobileSearchRef.current.getCurrentValue()
: ''
},
});
mobileSearchRef.current.setCurrentValue('');
};
return (
<React.Fragment>
<header id="page-topbar">
<div className="navbar-header">
<div className="d-flex">
<div className="dropdown d-inline-block d-lg-none ms-2">
<button
onClick={() => {
setSearch(!search);
}}
type="button"
className="btn header-item noti-icon mt-2"
id="page-header-search-dropdown"
>
<i className="mdi mdi-magnify" />
</button>
<div
className={
search
? 'dropdown-menu dropdown-menu-lg dropdown-menu-end p-0 show'
: 'dropdown-menu dropdown-menu-lg dropdown-menu-end p-0'
}
aria-labelledby="page-header-search-dropdown"
>
<SearchBar
id="headerSearchBar"
ref={mobileSearchRef}
onSubmitHandler={(e) => codeSearchHandler(e)}
/>
</div>
</div>
</div>
</div>
</header>
</React.Fragment>
);
};
export default Header;
If we look at the header component, we can see that we get the input value of search bar component using mobileSearchRef and getCurrentValue method. We can also set its value using setCurrentValue method.
You have to pass the ref aside the spread props:
export interface ButtonProps
extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement> {
children?: React.ReactNode;
loading?: boolean;
ref?: React.RefObject<HTMLButtonElement>
}
class Button extends React.Component<ButtonProps> {
render() {
const {
...otherProps,
ref
} = this.props;
return (
<button {...otherProps} ref={ref}></button>
)
}
}
export default Button;
const ForwardedElement = React.forwardRef<ButtonProps, HTMLButtonElement> (
(props: ButtonProps, ref) => <Button {...props} ref={ref}/>
)
export default ForwardedElement;
now it should work, see this question

How to update Draft Value when click of Save Button?

I have a two Input form and a Paragraph, when I try to change the value of input the paragraph get updated, once the paragraph is updated I am trying to edit the paragraph with the help of drafts library, but once I update the paragraph and save it, it doesn't update the paragraph.
Please anyone Help me out to solve the problem
Codesandbox Link : Code
Context API
import React, { useState, createContext } from "react";
export const Contx = createContext();
export const ConProvider = ({ children }) => {
const [customerName, setCustomerName] = useState("");
const [amount, setAmount] = useState("");
const defaultValue = `<p>Hello ${
customerName === "" ? "User" : customerName
},</p>
<p>Please Click on the link below to pay the order - <strong>${amount}</strong> . </p>
<p>Click hear to pay</p>
<br/>
<p>Thanks and Regards</p>
<p>testUser</p>`;
const [draftValue, setDraftValue] = useState(defaultValue);
return (
<Contx.Provider
value={{
defaultValue,
setCustomerName,
setAmount,
customerName,
amount,
setDraftValue,
draftValue
}}
>
{children}
</Contx.Provider>
);
};
homePage
import React, { useContext, useState } from "react";
import ReactDOM from "react-dom";
import { ConProvider, Contx } from "../ContextApi";
import Data from "./Component/Data/Data";
import NewDraft from "./Component/Data/NewDraft";
import Modal from "./Component/Data/Modal";
import "./styles.css";
function App() {
const { defaultValue, setDraftValue, draftValue } = useContext(Contx);
// console.log("defaultValue", defaultValue);
const [show, setShow] = useState(false);
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
return (
<div className="App">
<Data />
<Modal handleClose={handleClose} show={show}>
<NewDraft
prsFunc={setDraftValue}
handleClose={handleClose}
defaultValueEmpty={false}
defaultValue={defaultValue}
/>
</Modal>
<div
className="templateStyle p-2"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: draftValue && draftValue
}}
/>
<button onClick={handleShow}>Edit</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<ConProvider>
<App />
</ConProvider>,
rootElement
);
Input Form
import React, { useContext } from "react";
import { Contx } from "../../../ContextApi";
export default function Data() {
const {
setCustomerName,
setDraftValue,
defaultValue,
setAmount,
customerName,
amount
} = useContext(Contx);
React.useEffect(() => {
setDraftValue(defaultValue);
});
// console.log("fffffff", customerName, amount);
return (
<div>
<input
type="text"
value={customerName}
name="customerName"
placeholder="Enter Customer name"
onChange={(e) => {
setCustomerName(e.target.value);
}}
/>
<input
type="number"
placeholder="Enter Amount"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
</div>
);
}
DraftJS
import React, { useState } from "react";
import { Editor } from "react-draft-wysiwyg";
import {
EditorState,
convertToRaw,
ContentState,
convertFromHTML
} from "draft-js";
import "react-draft-wysiwyg/dist/react-draft-wysiwyg.css";
import draftToHtml from "draftjs-to-html";
import "./Data.css";
export default function NewDraft({
handleClose,
defaultValue,
defaultValueEmpty,
prsFunc
}) {
const initialState = defaultValueEmpty
? () => EditorState.createEmpty()
: EditorState.createWithContent(
ContentState.createFromBlockArray(convertFromHTML(defaultValue))
);
const [editorState, setEditorState] = useState(initialState);
const onChange = (value) => {
setEditorState(value);
};
const saveData = () => {
prsFunc(draftToHtml(convertToRaw(editorState.getCurrentContent())));
handleClose();
};
// console.log(draftToHtml(convertToRaw(editorState.getCurrentContent())));
return (
<div>
<div style={{ border: "2px solid", padding: "20px" }}>
<Editor
editorState={editorState}
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onEditorStateChange={(value) => onChange(value)}
/>
<button variant="secondary" onClick={saveData}>
Save
</button>
</div>
</div>
);
}
The problem you are facing is caused by this line in Data component. Every time the component is updated, the draftValue is set to the defaultValue.
React.useEffect(() => {
setDraftValue(defaultValue);
});

Resources