How to use react-dropzone with react-hook-form so that the form returns proper file - not just file name?
Here is a work from a react-hook-form Github discussion:
export const DropzoneField = ({
name,
multiple,
...rest
}) => {
const { control } = useFormContext()
return (
<Controller
render={({ onChange }) => (
<Dropzone
multiple={multiple}
onChange={e =>
onChange(multiple ? e.target.files : e.target.files[0])
}
{...rest}
/>
)}
name={name}
control={control}
defaultValue=''
/>
)
}
const Dropzone = ({
multiple,
onChange,
...rest
}) => {
const {
getRootProps,
getInputProps,
} = useDropzone({
multiple,
...rest,
})
return (
<div {...getRootProps()}>
<input {...getInputProps({ onChange })} />
</div>
)
}
You should check out Controller API as it was made to make integration with external controlled input easier. There are quite a few examples therein as well.
i did this way #Bill
const FileUpload = (props) => {
const {
control,
label,
labelClassName,
name,
isRequired,
rules,
error,
multiple,
maxFiles,
setValue,
accept,
maxSize,
setError,
clearErrors,
formGroupClassName,
watch,
} = props;
const [files, setFiles] = useState(watch(name));
const onDrop = useCallback(
(acceptedFiles, rejectedFiles) => {
if (rejectedFiles && rejectedFiles.length > 0) {
setValue(name, []);
setFiles([]);
setError(name, {
type: 'manual',
message: rejectedFiles && rejectedFiles[0].errors[0].message,
});
} else {
setFiles(
acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
}),
),
);
clearErrors(name);
acceptedFiles.forEach((file) => {
const reader = new FileReader();
reader.onabort = () => toastError('File reading was aborted');
reader.onerror = () => toastError('file reading has failed');
reader.readAsDataURL(file);
reader.onloadend = () => {
setValue(name, file, { shouldValidate: true });
};
});
}
},
[name, setValue, setError, clearErrors],
);
const deleteFile = (e, file) => {
e.preventDefault();
const newFiles = [...files];
newFiles.splice(newFiles.indexOf(file), 1);
if (newFiles.length > 0) {
setFiles(newFiles);
} else {
setFiles(null);
setValue(name, null);
}
};
const thumbs =
files &&
files !== null &&
files.map((file) => {
const ext = file.name && file.name.substr(file.name.lastIndexOf('.') + 1);
return ext === 'pdf' ? (
<ul key={file.name} className="mt-2">
<li>{file.name}</li>
</ul>
) : (
<div className="thumb position-relative" key={file.name}>
<img src={file.preview ? file.preview : file} alt={file.name} />
<Button
className="trash-icon"
color="danger"
size="sm"
onClick={(e) => deleteFile(e, file)}
>
<FontAwesomeIcon icon={faTrashAlt} size="sm" />
</Button>
</div>
);
});
useEffect(() => {
if (
watch(name) !== '' &&
typeof watch(name) === 'string' &&
watch(name).startsWith('/')
) {
setFiles([
{
preview: getFileStorageBaseUrl() + watch(name),
name: watch(name)
.substr(watch(name).lastIndexOf('/') + 1)
.substr(0, watch(name).lastIndexOf('/')),
},
]);
}
}, [watch, name]);
useEffect(
() => () => {
if (files && files.length > 0) {
files.forEach((file) => URL.revokeObjectURL(file.preview));
}
},
[files],
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
maxFiles: multiple ? maxFiles : 0,
accept,
onDrop,
minSize: 0,
maxSize,
multiple,
});
return (
<div className={formGroupClassName || 'file-input my-2 form-group'}>
{label && (
<label className={labelClassName} htmlFor={name}>
{label}
{isRequired && <span style={{ color: 'red' }}> * </span>}
</label>
)}
<Controller
control={control}
name={name}
rules={rules}
render={(controllerProps) => (
<div
{...getRootProps({
className: 'dropzone w-100 fs-20 d-flex align-items-center',
})}
{...controllerProps}
>
<input {...getInputProps()} />
<FontAwesomeIcon
icon={faCloudUploadAlt}
size="sm"
className="mr-1"
/>
{isDragActive ? (
<span className="fs-16">Drop the files here ... </span>
) : (
<span className="fs-16">Select files </span>
)}
</div>
)}
/>
<aside className="thumbs-container">{thumbs}</aside>
{error && <p className="form-error mb-0">{error.message}</p>}
</div>
);
};
here is the solution with v7
const DropzoneField = ({
name,
control,
...rest
}: {
name: string;
control: Control<FieldValues>;
}) => {
// const { control } = useFormContext();
return (
<Controller
render={({ field: { onChange } }) => (
<Dropzone onChange={(e: any) => onChange(e.target.files[0])} {...rest} />
)}
name={name}
control={control}
defaultValue=""
/>
);
};
const Dropzone = ({ onChange, ...rest }: { onChange: (...event: any[]) => void }) => {
const onDrop = useCallback((acceptedFiles) => {
// Do something with the files
console.log({ acceptedFiles });
}, []);
const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop });
return (
<div {...getRootProps()}>
<input {...getInputProps({ onChange })} />
{isDragActive ? (
<p>Drop the files here ...</p>
) : (
<p>Drag 'n' drop some files here, or click to select files</p>
)}
</div>
);
};
I got it working properly including with both drop and click to add a file using the following code:
FileInput.js
import React, { useCallback, useEffect } from "react"
import { useDropzone } from "react-dropzone"
import { useFormContext } from "react-hook-form"
const FileInput = props => {
const { name, label = name } = props
const { register, unregister, setValue, watch } = useFormContext()
const files = watch(name)
const onDrop = useCallback(
droppedFiles => {
setValue(name, droppedFiles, { shouldValidate: true })
},
[setValue, name]
)
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
accept: props.accept,
})
useEffect(() => {
register(name)
return () => {
unregister(name)
}
}, [register, unregister, name])
return (
<>
<label className=" " htmlFor={name}>
{label}
</label>
<div
{...getRootProps()}
type="file"
role="button"
aria-label="File Upload"
id={name}
>
<input {...props} {...getInputProps()} />
<div
style={{ width: "500px", border: "black solid 2px" }}
className={" " + (isDragActive ? " " : " ")}
>
<p className=" ">Drop the files here ...</p>
{!!files?.length && (
<div className=" ">
{files.map(file => {
return (
<div key={file.name}>
<img
src={URL.createObjectURL(file)}
alt={file.name}
style={{
height: "200px",
}}
/>
</div>
)
})}
</div>
)}
</div>
</div>
</>
)
}
export default FileInput
Form
import React from "react"
import { FormProvider, useForm } from "react-hook-form"
import FileInput from "./FileInput"
const Form = () => {
const methods = useForm({
mode: "onBlur",
})
const onSubmit = methods.handleSubmit(values => {
console.log("values", values)
})
return (
<FormProvider {...methods}>
<form onSubmit={onSubmit}>
<div className="">
<FileInput
accept="image/png, image/jpg, image/jpeg, image/gif"
name="file alt text"
label="File Upload"
/>
</div>
</form>
</FormProvider>
)
}
export default Form
Related
When I try to make a blog, I can't pass the editor in the form. I found this:
DraftJS React-Hook-Form - submitting Editor as input
but it seems that LexicalRichTextEditor does not have such a tag to pass. Can anyone help me?
How can I pass properties to achieve the Add Content and Modify Content functionality?
type LexicalEditorProps = {
//config: Parameters<typeof LexicalComposer>["0"]["initialConfig"];
content: any;
};
export default function MyEditor(props: LexicalEditorProps) {
const [ editor ] = useLexicalComposerContext();
const editorStateRef = useRef();
const [saveContent, setSaveContent] = useState('');
const editorConfig: any = {
// The editor theme
theme: EditorTheme,
// Handling of errors during update
onError(error: any) {
throw error;
},
editorState: props.content,
// Any custom nodes go here
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode
]
};
useEffect(()=>{
if(editorStateRef.current){
setSaveContent(JSON.stringify(editorStateRef.current));
}
editor.update(()=>{
const root = $getRoot();
const selection = $getSelection();
const paragraphNode = $createParagraphNode();
const textNode = $createTextNode(saveContent);
paragraphNode.append(textNode);
root.append(paragraphNode);
});
},[saveContent]);
return (
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
<ToolbarPlugin />
<div className="editor-inner">
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<Placeholder />}
ErrorBoundary={LexicalErrorBoundary}
/>
<OnChangePlugin onChange={(editorState:any) => editorStateRef.current = editorState} />
<HistoryPlugin />
<AutoFocusPlugin />
<CodeHighlightPlugin />
<ListPlugin />
<LinkPlugin />
<AutoLinkPlugin />
<ListMaxIndentLevelPlugin maxDepth={7} />
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
</div>
</div>
</LexicalComposer>
);
}
export function MyForm(){
const {register, handleSubmit, control, formState: {errors}} = useForm();
const onSubmit = ( data:any) => {
console.log(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={2}>
<Card>
<Controller control={control} name="content" render={()=> (
<MyEditor content={dataSet.content} />
)} />
</Card>
<Box>
<Button variant="contained" type="submit">Save</Button>
</Box>
</Stack>
</form>
);
}
I got the solution, but it's not my doing, it's the great others who helped me with the problem and hopefully will help others.
https://codesandbox.io/s/purple-water-xf50bi?file=/src/App.tsx
export default function App() {
const schema = yup
.object({
title: yup.string().required(),
category: yup.string().required(),
tags: yup.array().required()
})
.required();
const { register, handleSubmit } = useForm({
resolver: yupResolver(schema)
});
const onSubmit = (data: any) => {
console.log(data);
};
const editorRef: any = useRef();
// ADDED THIS:
if (editorRef.current !== undefined) {
if (editorRef.current !== null) {
const latestEditorState = editorRef.current.getEditorState();
const textContent = latestEditorState.read(() =>
//You could change getTextContent() for your purpose
$getRoot().getTextContent()
);
console.log(textContent);
}
}
return (
<div className="App">
<form onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={2}>
<input {...register("title")} type="text" placeholder="Title" />
<input {...register("category")} type="text" placeholder="Category" />
<input type="" placeholder="Tags" />
<select {...register("tags")} id="tags" multiple>
<option value="nginx">nginx</option>
<option value="java">java</option>
<option value="react">react</option>
<option value="mui">mui</option>
</select>
<Card elevation={3}>
<MyEditor ref={editorRef} />
</Card>
<button type="submit">Save</button>
</Stack>
</form>
</div>
);
}
MyEditor.tsx
function Placeholder() {
return <div className="editor-placeholder">Enter some rich text...</div>;
}
const editorConfig: any = {
// The editor theme
theme: EditorTheme,
// Handling of errors during update
onError(error: any) {
throw error;
},
// Any custom nodes go here
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode
]
};
// ADDED THIS:
const EditorCapturePlugin = React.forwardRef((props: any, ref: any) => {
const [editor] = useLexicalComposerContext();
useEffect(() => {
ref.current = editor;
return () => {
ref.current = null;
};
}, [editor, ref]);
return null;
});
export const MyEditor = React.forwardRef((props: any, ref: any) => {
return (
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
<ToolbarPlugin />
<div className="editor-inner">
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<Placeholder />}
ErrorBoundary={LexicalErrorBoundary}
/>
{/* ADDED THIS: */}
<EditorCapturePlugin ref={ref} />
<HistoryPlugin />
<AutoFocusPlugin />
<CodeHighlightPlugin />
<ListPlugin />
<LinkPlugin />
<AutoLinkPlugin />
<ListMaxIndentLevelPlugin maxDepth={7} />
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
</div>
</div>
</LexicalComposer>
);
});
Not pretty but it works great, in this case I need to store both the editorState (to be able to edit the field after saving it in the db) and the content itself in html
<Controller
control={control}
name={`${fieldName}.payload.content`}
render={({ field: fieldContent }) => (
<Controller
control={control}
name={`${fieldName}.payload.editorState`}
render={({ field: fieldEditorState }) => (
<RichText
background={backgroundColor}
placeholder={t('create-campaign-form:create-text-for-campaign')}
onChange={value => {
fieldEditorState.onChange(value.editorState)
fieldContent.onChange(value.content)
}}
value={fieldEditorState.value}
/>
)}
/>
)}
/>
The properties value and onChange are used like so
// RichText.tsx
const RichText = ({
placeholder = '',
onChange,
value,
background,
}: EditorProps) => {
const placeholderElement = <PlaceholderElement placeholder={placeholder} />
const [floatingAnchorElem, setFloatingAnchorElem] =
useState<HTMLDivElement | null>(null)
const onRef = (_floatingAnchorElem: HTMLDivElement) => {
if (_floatingAnchorElem !== null) {
setFloatingAnchorElem(_floatingAnchorElem)
}
}
const initialConfig: InitialConfigType = {
namespace: 'RichTextEditor',
nodes: [...Nodes],
onError: (error: Error) => {
throw error
},
theme,
...(value && {
editorState: typeof value !== 'string' ? JSON.stringify(value) : value,
}),
}
return (
<LexicalComposer initialConfig={initialConfig}>
<ToolbarPlugin />
<EditorContainer background={background}>
<LexicalAutoLinkPlugin />
<RichTextPlugin
contentEditable={
<EditorScroller>
<EditorWrapper ref={onRef}>
<ContentEditable className={'ContentEditable__root'} />
</EditorWrapper>
</EditorScroller>
}
placeholder={placeholderElement}
ErrorBoundary={null}
/>
<LinkPlugin />
<ClickableLinkPlugin />
{floatingAnchorElem && (
<FloatingLinkEditorPlugin anchorElem={floatingAnchorElem} />
)}
</EditorContainer>
<OnChangePlugin onChange={onChange} />
</LexicalComposer>
)
}
// plugins/onChange.tsx
interface onChangePluginProps {
onChange: (...event: any[]) => void
}
export default function OnChangePlugin({
onChange,
}: onChangePluginProps): JSX.Element {
return (
<LexicalOnchangePlugin
onChange={(editorState: EditorState, editor: LexicalEditor) => {
editorState.read(() => {
const htmlString = $generateHtmlFromNodes(editor, null)
onChange({ content: htmlString, editorState })
})
}}
/>
)
}
i have a problem.
I use a form to edit my data, then when i want to see edited data, i get an ×
TypeError: Cannot read properties of undefined (reading 'id')
Pointing at my
{users &&
users.map((user) => {
return (
<div key={user.id}>
<Link to={`users/${user.id}`}> {user.name} </Link>
</div>
);
})}
Which is used to display data.
After refreshing the site (F5) it works, so i assume that the redux has problem with reading edited data for the first time, altough it do work with adding new data. anyone know what i can do?
My UserEditForm:
const UserEditForm = () => {
let { id } = useParams();
const { user } = useSelector((state) => state.user);
const [state, setState] = useState({
name: "",
birthday: "",
img: "",
});
const [error, setError] = useState("");
console.log(id);
let history = useHistory();
let dispatch = useDispatch();
const { name, birthday, img } = state;
useEffect(() => {
dispatch(getSingleUser(id));
}, []);
useEffect(() => {
if (user) {
setState({ ...user });
}
}, [user]);
const handleInputChange = (e) => {
let { name, value } = e.target;
setState({ ...state, [name]: value });
};
const handleSubmit = (e) => {
dispatch(updateUser(state, id));
history.push("/");
setError("");
};
return (
<div>
<Button
style={{ width: "100px", marginTop: "20px" }}
variant="contained"
color="secondary"
onClick={() => history.push("/")}
>
Go Back
</Button>
<h2>Edit User</h2>
{error && <h3 style={{ color: "red" }}>{error}</h3>}
<form noValidate autoComplete="off" onSubmit={handleSubmit}>
<TextField
id="standard-basic"
label="Name"
value={name || ""}
name="name"
type="text"
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
label="birthday"
name="birthday"
value={birthday || ""}
type="birthday"
onChange={handleInputChange}
/>
<br />
<TextField
id="standard-basic"
label="img"
value={img || ""}
name="img"
type="number"
onChange={handleInputChange}
/>
<Button
style={{ width: "100px" }}
variant="contained"
color="primary"
type="submit"
onChange={handleInputChange}
>
Update
</Button>
</form>
</div>
);
};
export default UserEditForm;
My UserList component:
const UserList = ({ users, history }) => {
const dispatch = useDispatch();
const fetchUsers = async () => {
const response = await axios
.get("http://localhost:3000/characters")
.catch((err) => {
console.log("Err: ", err);
});
dispatch(setUsers(response.data));
};
useEffect(() => {
fetchUsers();
}, []);
console.log(users);
return (
<div>
<button onClick={() => history.goBack()}>...back</button>
<li>
<Link to="/user/add">Add Users</Link>
</li>
{users &&
users.map((user) => {
return (
<div key={user.id}>
<Link to={`users/${user.id}`}> {user.name} </Link>
</div>
);
})}
</div>
);
};
const mapStateToProps = (state) => {
return {
users: state.allUsers.users,
};
};
export default connect(mapStateToProps, null)(UserList);
Why the function onSubmit does not get triggered in my code? (I need to keep it all as it is, or ar least to function in the same manner, the Input tag I have managed by Controller, needs to show a state "price" all the time, which is changed by two functions (handlePriceInputChange, handleSelectedBox)
const schema = yup
.object({
voucherPrice: yup.number().positive().required(),
})
.required();
function PriceSelection(props) {
const {
register,
handleSubmit,
control,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
});
const onSubmit = (data) => {
console.log("does not work?", data);
};
const classes = useStylesPriceSelection();
const [selected, setSelected] = useState(false);
const [price, setPrice] = useState("");
const handleSelectedBox = (ev) => {
console.log("", price);
setSelected(ev.target.getAttribute("data-boxprice"));
setPrice(parseInt(ev.target.getAttribute("data-price")));
};
const handlePriceInputChange = (ev) => {
console.log("change", price);
setPrice(parseInt(ev.target.value));
};
const priceBox1 = 25;
const priceBox2 = 50;
const priceBox3 = 75;
const priceBox4 = 100;
return (
<div>
<section className={classes.priceBoxWrap}>
<div
data-boxprice="1"
data-price={`${priceBox1}`}
onClick={(ev) => handleSelectedBox(ev)}
className={clsx(
classes.priceBox,
((selected === "1" && price === priceBox1) ||
price === priceBox1) &&
classes.priceBoxSelected
)}
>
{`${priceBox1}€`}
</div>
<div
data-boxprice="2"
data-price={`${priceBox2}`}
onClick={(ev) => handleSelectedBox(ev)}
className={clsx(
classes.priceBox,
((selected === "2" && price === priceBox2) ||
price === priceBox2) &&
classes.priceBoxSelected
)}
>
{`${priceBox2}€`}
</div>
</section>
<section className={classes.priceBoxWrap}>
<div
data-boxprice="3"
data-price={`${priceBox3}`}
onClick={(ev) => handleSelectedBox(ev)}
className={clsx(
classes.priceBox,
((selected === "3" && price === priceBox3) ||
price === priceBox3) &&
classes.priceBoxSelected
)}
>
{`${priceBox3}€`}
</div>
<div
data-boxprice="4"
data-price={`${priceBox4}`}
onClick={(ev) => handleSelectedBox(ev)}
className={clsx(
classes.priceBox,
((selected === "4" && price === priceBox4) ||
price === priceBox4) &&
classes.priceBoxSelected
)}
>
{`${priceBox4}€`}
</div>
</section>
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="voucherPrice"
control={control}
defaultValue={false}
rules={{ required: true }}
render={({ field: { onChange, value, ref, ...field } }) => (
<Input {...field} onChange={(ev) => handlePriceInputChange(ev)} value={price} type="number" innerRef={ref} />
)}
/>
<p>{errors.voucherPrice?.message}</p>
<Button
variant="contained"
sx={{ mt: 1, mr: 1 }}
type="submit"
>
{"Continue"}
</Button>
</form>
</div>
);
}
export default PriceSelection;
You can just use react-hook-form for this situation and can get rid of the additional local state management via useState. I reworked your example and removed the data attributes and just passed the price values for each box via the onClick callback.
const schema = yup
.object({
voucherPrice: yup.number().positive().required()
})
.required();
const priceBox1 = 25;
const priceBox2 = 50;
const priceBox3 = 75;
const priceBox4 = 100;
const selectedStyles = { outline: "2px solid green" };
function PriceSelection(props) {
const {
handleSubmit,
control,
formState: { errors },
setValue,
watch
} = useForm({ resolver: yupResolver(schema) });
const voucherPrice = watch("voucherPrice");
const onSubmit = (data) => {
console.log(data);
};
const setVoucherPrice = (price) => () => setValue("voucherPrice", price);
return (
<div>
<section>
<div
className="pricebox"
style={voucherPrice === priceBox1 ? selectedStyles : null}
onClick={setVoucherPrice(priceBox1)}
>
{`${priceBox1}€`}
</div>
<div
className="pricebox"
style={voucherPrice === priceBox2 ? selectedStyles : null}
onClick={setVoucherPrice(priceBox2)}
>
{`${priceBox2}€`}
</div>
</section>
<section>
<div
className="pricebox"
style={voucherPrice === priceBox3 ? selectedStyles : null}
onClick={setVoucherPrice(priceBox3)}
>
{`${priceBox3}€`}
</div>
<div
className="pricebox"
style={voucherPrice === priceBox4 ? selectedStyles : null}
onClick={setVoucherPrice(priceBox4)}
>
{`${priceBox4}€`}
</div>
</section>
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
name="voucherPrice"
control={control}
rules={{ required: true }}
defaultValue={0}
render={({ field: { ref, ...field } }) => (
<Input {...field} type="number" innerRef={ref} />
)}
/>
<p>{errors.voucherPrice?.message}</p>
<Button variant="contained" sx={{ mt: 1, mr: 1 }} type="submit">
{"Continue"}
</Button>
</form>
</div>
);
}
If the handleSubmit function does not call e.preventDefault your page will refresh, to fix this you can wrap the handler like this:
...
const _handleSubmit = (e) => {
e.preventDefault()
handleSubmit(e)
}
...
<form onSubmit={_handleSubmit}>
...
I ‘m new in react js
I put a counter up in my form I have problem to put the result of counter with the form result in the databases
I use for my Api mongodb and spring boot
Count up function
export default function Index(props) {
/* Server State Handling */
const [count, setCount] = useState(0);
const [delay, setDelay] = useState(1000);
useInterval(() => setCount((c) => c + 1), delay);
useEffect(() => {
if (delay === null) alert(`Count ${count}`);
}, [count, delay]);
function useInterval(callback, delay) {
const savedCallback = useRef();
useEffect(() => {
savedCallback.current = callback;
});
useEffect(() => {
let id;
if (delay) {
id = setInterval(savedCallback.current, delay);
}
return () => clearInterval(id);
}, [delay]);
}
const [serverState, setServerState] = useState();
const handleServerResponse = (ok, msg) => {
setServerState({ok, msg});
};
Function of submitting data
const handleOnSubmit = (values, actions) => {
axios.post( "/consultations", values,
)
.then(response => {
props.history.push("/listcons");
actions.setSubmitting(false);
actions.resetForm();
handleServerResponse(true, "Thanks!");
alert("Consultation enregister avec succer");
})
.catch(error => {
actions.setSubmitting(false);
handleServerResponse(false, error.response.data.error);
});
My form with the counter up
I have problem here when I submitted my form in my databases the count value is always 0 (the initial value) how can I resolve this problem what is the problem of my code
return (
<div className="container ">
<div className="card-body bg-white">
<Formik
initialValues={{count:0,titre: ""}}
onSubmit={handleOnSubmit}
validationSchema={formSchema}
>
{({ isSubmitting }) => (
<Form id="fs-frm" noValidate >
<div style={{textAlign: 'center'}}>
<h3>Count</h3>
<h2> {count}</h2>
</div>
<div className="form-group" >
<label htmlFor="titre">Titre</label>
<Field id="titre" name="titre" className="form-control" />
<ErrorMessage name="titre" className="errorMsg" component="p" />
</div>
<Card.Footer style={{ "textAlign": "right" }}>
<button type="submit" className="btn btn-primary" disabled={isSubmitting}>
Submit
</button>
</Card.Footer>
{serverState && (
<p className={!serverState.ok ? "errorMsg" : ""}>
{serverState.msg}
</p>
)}
</Form>
)}
</Formik>
</div>
</div>
);
};
I am using Material UI stepper and on each step I have different components. I am trying to validate each step using Yup and have used Formik but the instead of validating it moves on to next step. How can I validate this on every step and can get all the data of each step at last.
import React from 'react';
..
import FormStep1 from './FormStep1';
import FormStep2 from './FormStep2';
function getSteps() {
return ['Select general campaign settings', 'Ads setting', 'Upload Ad contents', 'Review and Submit'];
}
function getStepContent(stepIndex, handleStepSubmit, handleNext) {
switch (stepIndex) {
case 0:
return <FormStep1 onSubmit={handleStepSubmit} onNext={handleNext}/>;
case 1:
return <FormStep2 />;
case 2:
return 'No need to worry abt this!';
default:
return 'Unknown stepIndex';
}
}
const IobdCampCreate = () => {
const classes = useStyles();
const [activeStep, setActiveStep] = React.useState(0);
const [state, setState] = React.useState({
steps: [
{ name: 'Select general campaign settings', data: {} },
{ name: 'form2', data: {} }
],
activeStep: 0,
});
const handleNext = () => {
setActiveStep((prevActiveStep) => prevActiveStep + 1);
};
const handleBack = () => {
setActiveStep((prevActiveStep) => prevActiveStep - 1);
};
const handleReset = () => {
setActiveStep(0);
};
const handleStepSubmit = (stepIndex, data) => {
console.log("-------", stepIndex, data)
setState((prevState) => ({
...prevState,
activeStep: prevState.activeStep + 1,
steps: prevState.steps.map((step, index) => {
if (stepIndex !== index) {
return step;
}
return {
...step,
data
}
})
}))
}
return (
<React.Fragment>
<div className={classes.root}>
<Stepper activeStep={activeStep} alternativeLabel>
{state.steps.map((label) => (
<Step key={label.name}>
<StepLabel>{label.name}</StepLabel>
</Step>
))}
</Stepper>
<div>
{activeStep === state.steps.length ? (
<div>
<Typography className={classes.instructions}>All steps completed</Typography>
<Button onClick={handleReset}>Reset</Button>
</div>
) : (
<div>
<div className={classes.instructions}>
{getStepContent(activeStep, handleStepSubmit,handleNext )}
</div>
<div>
<Button
disabled={activeStep === 0}
onClick={handleBack}
className={classes.backButton}
>
Back
</Button>
<Button variant="contained" color="primary" onClick={handleNext}>
{activeStep === state.steps.length - 1 ? 'Finish' : 'Next'}
</Button>
</div>
</div>
)}
</div>
</div>
</React.Fragment>
);
};
export default IobdCampCreate;
Here is a the formstep1.js
import React from 'react';
import { Formik } from 'formik';
const FormStep1 = (props) => (
<div>
<h3>Form A</h3>
<Formik
initialValues={{ email: '', password: '' }}
validate={values => {
const errors = {};
if (!values.email) {
errors.email = 'Required';
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
) {
errors.email = 'Invalid email address';
}
return errors;
}}
onSubmit={(values) => {
props.onSubmit(0, values);
}}
>
{({
values,
errors,
touched,
handleChange,
handleBlur,
handleSubmit,
isSubmitting,
/* and other goodies */
}) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
onBlur={handleBlur}
value={values.email}
/>
{errors.email && touched.email && errors.email}
<input
type="password"
name="password"
onChange={handleChange}
onBlur={handleBlur}
value={values.password}
/>
{errors.password && touched.password && errors.password}
<button variant="contained" color="primary" type="submit" >
Next
</button>
</form>
)}
</Formik>
</div>
);
export default FormStep1;