react hook form not getting context on nested component - reactjs

I am having multiple tab, each tabs load different components. In each components I have different set of forms. So there is a generic footer where user can click on save or continue.
So I am trying to use the react hook form useContext, but I am not getting the values.
I have reproduced the same issue on the below code.
SaveAndContinue component
import React from "react";
import { useFormContext } from "react-hook-form";
const Footer = (props) => {
const { formState, handleSubmit } = useFormContext();
const onSaveDetails = (data) => {
console.log("onSaveDetails", data);
};
const onContinue = (data) => {
console.log("onContinue", data);
};
return (
<>
<button
disabled={!formState?.isDirty}
onClick={handleSubmit(onSaveDetails)}
>
Save
</button>
<button disabled={!formState?.isDirty} onClick={handleSubmit(onContinue)}>
Continue
</button>
</>
);
};
export default Footer;
How can I get the formData of each component form when clicking on the save or continue button using react hook form context
Any help is appreciated
Here is the codesandbox

I looked at your codesandbox. the problem is:
on your ServiceDetails and UserDetails components you should register your inputs using useFormContext not useForm:
const { register } = useFormContext({
mode: "onBlur",
reValidateMode: "onChange",
defaultValues: {
firstName: ""
}
});

Related

How can I use a toast of Chakra UI throughout all the components?

I am trying to show an alert when user makes any request in my react application. The thing is that currently I am using separate toast components for separate components. How should I use one single toast component throughout the whole application. I tried putting the toast component in App.jsx but in order to manage the toast message and color I have to do prop-drilling, which I want to avoid. I am using redux so I can not use useContext for managing the toast. Any idea would be appreciated.
I prefer using a higher-order component called HOC to wrap the toast and provide the necessary props to it. This way, you can keep the toast in a central location and use the HOC to wrap other components that need to display the toast.
For example:
// withToast.js
import { useState } from "react";
import { ToastProvider } from "#chakra-ui/core";
const withToast = (WrappedComponent) => {
return (props) => {
const [toast, setToast] = useState({
message: "",
color: "",
isOpen: false,
});
const showToast = (message, color) => {
setToast({ message, color, isOpen: true });
};
const hideToast = () => {
setToast({ message: "", color: "", isOpen: false });
};
return (
<ToastProvider>
<WrappedComponent
{...props}
showToast={showToast}
hideToast={hideToast}
toast={toast}
/>
</ToastProvider>
);
};
};
export default withToast;
Now you can use the same toast in every component that is being wrapped by withToast:
import React from 'react';
import withToast from './withToast';
const App = (props) => {
const { showToast, toast } = props;
return (
<div>
<button onClick={() => showToast("Hello, World!", "green")}>
Show Toast
</button>
<Toast message={toast.message} color={toast.color} isOpen={toast.isOpen} />
</div>
);
};
export default withToast(App);
You can also wrap multiple components in the HOC and use the showToast and hideToast functions in any component that is rendered within the wrapped component, this way you don't have to prop-drill showToastand hideToast.

React-Admin custom login page and React Hook

I'm using React-Admin 3.14 and I would like to have a custom login page. When I use the one below with the useLogin hook, I have a hook related error which I can't figure out.
import React from 'react';
import { useLogin,useNotify } from 'react-admin';
import Button from '#material-ui/core/Button'
const LoginPage = () => {
const HandleClick = () => {
console.log('Clicked login ...');
const login = useLogin;
const notify = useNotify;
login({ username:'john', password:'doe' }).catch(() => notify("Invalid email or password"));
}
return(
<Button variant="contained" onClick={HandleClick}>Login</Button>
);
}
export default LoginPage;
UseLogin is a callback to the login method of the Reac-Admin 3.14 authProvider (https://marmelab.com/react-admin/doc/3.14/Authentication.html#uselogin-hook).
The error that I get is:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.
I think I must be breacking Rules of Hooks somewhere ... but which one?
Thanks for your help.
C
Your useLogin and useNotify not executed
Change const login = useLogin to const login = useLogin()
Change const notify = useNotify to const notify = useNotify()
import React from 'react';
import { useLogin,useNotify } from 'react-admin';
import Button from '#material-ui/core/Button'
const LoginPage = () => {
const login = useLogin();
const notify = useNotify();
const HandleClick = () => {
console.log('Clicked login ...');
login({ username:'john', password:'doe' }).catch(() => notify("Invalid email or password"));
}
return(
<Button variant="contained" onClick={HandleClick}>Login</Button>
);
}
export default LoginPage;

Can't call setState on a component that is not yet mounted. - React

I am creating a React component using an npm wysiwyg. When the page loads, I grab some user data from a context I have set up. I grab the user's name and cover letter which is some html code. When the page loads I want to change the wysiwyg's state to contain the cover letter that way it displays it to the user. It works the first time the page loads, however if I hit refresh or try to open the page again, the contents of the wysiwyg disappear. Upon inspection of the console, I am met with a
Warning: Can't call `setState` on a component that is not yet mounted. This is a no-op, but it might indicate a bug in your application. Instead, assign to `this.state` directly or define a `state = {};` class property with the desired state in the n component.
I am unsure of what I am doing wrong.
Here is my component:
import React, { useContext, useEffect, useState } from 'react';
import { LoggedInContext } from './contexts/LoggedIn';
import { Editor } from 'react-draft-wysiwyg';
import { EditorState, ContentState } from 'draft-js';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import htmlToDraft from 'html-to-draftjs';
import './Profile.css';
const ProfileSettings = () => {
const { user, coverLetter } = useContext(LoggedInContext);
const blocksFromHtml = htmlToDraft(coverLetter);
const { contentBlocks, entityMap } = blocksFromHtml;
const contentState = ContentState.createFromBlockArray(
contentBlocks,
entityMap
);
const editorState = EditorState.createWithContent(contentState);
const [editorStateReact, setEditorStateReact] = useState(editorState);
const onEditorStateChange = (editorState) => {
setEditorStateReact(editorState);
};
return (
<div className="Profile">
<h2>User: {user}</h2>
<Editor
editorState={editorStateReact}
toolbarClassName="toolbarClassName"
wrapperClassName="wrapperClassName"
editorClassName="editorClassName"
onEditorStateChange={onEditorStateChange}
/>
<button className="btn btn-lg btn-primary my-3">Save Profile</button>
</div>
);
};
export default ProfileSettings;

React Little State Machine clear data

state-machine with react-hook-form to make my forms, but after submit the form i want to clear the storage after submit;
This is how i create my store;
createStore({
data: {}
});
And this is my Submit function
const onSubmit = (data:any) => {
action(data);
props.onSubmit(state.data);
// I need a function to clear the data to not complete my forms after i submit
}
Here a little example that i want:
https://codesandbox.io/s/delete-data-little-state-machine-q3w0g
In "step3"i want to clear the data after click on button
It looks like you need to create another action and pass that to the hook. You can see examples of this in the docs. Here is a working example:
clearAction.js
export default function clearAction(state, payload) {
return {
data: {}
};
}
Step3.js
import React from "react";
import { useForm } from "react-hook-form";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import clearAction from "./clearAction";
const Step3 = (props) => {
const { register, handleSubmit } = useForm();
const { state, action } = useStateMachine(clearAction);
const onSubit = (data) => {
action(data);
props.history.push("./resultFinal");
console.log(state, action);
action();
};
return (
<form onSubmit={handleSubmit(onSubit)}>
<h2>Clear Data</h2>
<input type="submit" />
</form>
);
};
export default withRouter(Step3);
Note in the examples provided in the docs you can pass multiple actions to the hook as needed.

React: A service returning a ui component (like toast)?

Requirement: Show toast on bottom-right corner of the screen on success/error/warning/info.
I can create a toast component and place it on any component where I want to show toasts, but this requires me to put Toast component on every component where I intend to show toasts. Alternatively I can place it on the root component and somehow manage show/hide (maintain state).
What I am wondering is having something similar to following
export class NotificationService {
public notify = ({message, notificationType, timeout=5, autoClose=true, icon=''}: Notification) => {
let show: boolean = true;
let onClose = () => {//do something};
if(autoClose) {
//set timeout
}
return show ? <Toast {...{message, notificationType, onClose, icon}} /> : </>;
}
}
And call this service where ever I need to show toasts.
Would this be the correct way to achieve the required functionality?
You can use AppContext to manage the state of your toast and a hook to trigger it whenever you want.
ToastContext:
import React, { createContext, useContext, useState } from 'react';
export const ToastContext = createContext();
export const useToastState = () => {
return useContext(ToastContext);
};
export default ({ children }) => {
const [toastState, setToastState] = useState(false);
const toastContext = { toastState, setToastState };
return <ToastContext.Provider value={toastContext}>{children}</ToastContext.Provider>;
};
App:
<ToastProvider>
<App/>
<Toast show={toastState}/>
</ToastProvider>
Then anywhere within your app you can do:
import {useToastState} from 'toastContext'
const {toastState, setToastState} = useToastState();
setToastState(!toastState);

Resources