react-admin: `data` is undefined in onSuccess() on <Edit> component - react-admin

I'm trying to follow the example from https://marmelab.com/react-admin/CreateEdit.html#onsuccess.
I'd like to notify users with the record title on update.
const onSuccess = ({ data }) => {
console.log(data) // output: undefined
notify(`Changes to post "${data.title}" saved`)
redirect('/posts');
refresh();
};
JS always complains that undefined does not have a title property.
According to the docs, data comes from the response of dataProvider.update()
The onSuccess function receives the response from the dataProvider call (dataProvider.create() or dataProvider.update())...
But, from what I tested, onSuccess() is called right away on submit, and dataProvider.update() is called when the toast message disappears, so onSuccess() does not receive the result of the Promise returned by dataProvider.update().
Is there something I'm missing in the example?
Additional informations
I'm using react-admin v3.11.1 and Chrome 87.0.4280.88
The record is updated correctly if I remove onSuccess() from the Component

If you are in Edit context you have to set undoable={false} to the Edit component to prevent optimistic response that does not wait for the request response.
<Edit
{...props}
onSuccess={onSuccess}
undoable={false}
>
...
</Edit>
Ref: https://marmelab.com/react-admin/CreateEdit.html#undoable

Related

Submitting Formik form in React Native

I am building a React Native app that uses Formik. When I submit the form I call handleSubmit
<Formik
onSubmit={values => {
handleSubmit(values)
}}>
I define this before the return on my form:
const handleSubmit = (values) => {
const { status, data } = usePostRequest("/api/holidays-request", {
dateFrom: "2023-02-01",
dateTo: "2023-02-28",
fromHalf: 0,
toHalf: 0,
});
};
I have hard coded some values here for testing.
My usePostRequest is a custom hook I wrote to actually send the data to my API.
When I submit my form then handleSubmit is triggered but I get an erorr:
Warning: An unhandled error was caught from submitForm() [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
It must be reason 2 that I am failing but I am unsure how to use my usePostRequest to submit the values from the form.
I would recommend you take a look at the custom hooks documentation
In this particular case you should write the post logic as a function and use it here directly.
Do not call hooks in event handlers.
const handleSubmit = (values) => {
const { status, data } = usePostRequest("/api/holidays-request", {
dateFrom: "2023-02-01",
dateTo: "2023-02-28",
fromHalf: 0,
toHalf: 0,
});
};
See Invalid Hook Call Warning for details.

Handling Response Text in React Admin

I am using React-Admin and Postgresql.
I'm looking to capture the response data that I am sending in a failed post response and display that in the UI, but I have no idea if this is possible or not. I see some of the react-admin api for onFailure and onSuccess but I can't make it do what I want.
My response is
{'data': failure_reasons}, HTTPStatus.HTTP_I_AM_A_TEAPOT
Failure_reasons can be whatever you want...a dict or a list.
I'm super new to react-admin and javascript so keep that in mind. Thanks.
Actually onSuccess and onError seems to be the good way to achieve this:
const handleFailure = (error) => {
// handle you error and parse it here
notify(`Something bad happened: ${error}`), 'error');
};
const handleSuccess = (data) => {
notify('Success!');
redirect('show', props.basePath, data.id);
refresh();
};
return (
<Edit
{...props}
onFailure={handleFailure}
onSuccess={handleSuccess}
>
...
</Edit>
);

API response is not accessible in ComponentDidMount but in render I can use

Hi I am working on React Native app. I am using Redux and Saga. I call the API in componentDidMount.
async componentDidMount() {
let data = this.props.navigation.getParam("returnProductData");
if (data) {
console.log("Return Here");
this.props.getProductReturnAction(data)
this.setState({
returnQty:parseInt(this.props.product.item_ordered)-parseInt(this.props.product.already_return_qty)
});
console.log(this.state.returnQty,"Return quty"); //coming undefined
console.log(this.props.product, "product"); // undefined
console.log(this.props.product.item_ordered); //undefined
}
}
I have to set the state in componentDidMount for returnQty. But, state is not accessible here. It's working fine in render method. I can use all the product object. But, it is coming empty in componentDidMount. I tried using async and await but it's not working.
// Dispatch Methods
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{ getProductReturnAction, submitProductReturnAction },
dispatch
);
};
// Props
const mapStateToProps = state => {
return {
product: state.myOrdersReducer.returnProduct
};
};
I can't be able to find out the bug please help to find out the best solution.
When you are making API calls through redux/saga, you can not use async await, as the frameworks will just dispatch an action and return back, the listeners which are registered for the action will be triggered and then after they complete their work they will dispatch a new action and respect reducer will handle the response.
Explained above is general scenario.
In your scenario,
You are dispatching the action returned by getProductReturnAction which will give say GET_PRODUCTS action.
A saga would be registered for GET_PRODUCTS, say getProducts, this get invoked.
This will perform the API call once the response is received it will dispatch GET_PRODUCTS_SUCCESS along with the products data.
Corresponding reducer which handles GET_PRODUCTS_SUCCESS will get called and that updates returnProduct and as you are registered for that in your component the render method gets called (as the props are changed) and hence product data is available in your render method.
This is working perfectly correct. I don't see anything wrong here.
As the data is available in props use the same u do not need to do a setState again on that.

Can you disable the React-Admin Undo feature in config?

The Undo feature is a great, but it can cause inefficiencies during development cycles.
Is there an easy way for us to disable it in our staging environment, or at least lower the timeout?
The Edit and Create components support the undoable parameter. So you could do like this <Edit {...props} undoable={false} > to disable the undo function for a specific Form
When I unterstand the documentation and src correctly, you have to override the notification component in order to change the autoHideDuration.
This is the time the notification is visible to the user and after the delay, the request is send to the api.
When you set it to 0 the requests should be send nearly immediately.
From the documentation - Theming - Notifications:
You can override the notification component, for instance to change
the notification duration. It defaults to 4000, i.e. 4 seconds, and
you can override it using the autoHideDuration prop. For instance, to
create a custom Notification component with a 5 seconds default:
// in src/MyNotification.js
import { Notification } from 'react-admin';
const MyNotification = props => <Notification {...props}autoHideDuration={5000} />;
export default MyNotification;
undoable can only be set on Edit component and not on the Create component.
Handle the formProps coming from the Create page by adding a custom variable to check if the props are indeed from the 'Create' page or from the server.
To customize notification for Create or Edit page you can pass the successMessage prop to the components
successMessage="Item created successfully" //or use translate
More about 'successMessage' can be found here - React Documentation
With react-admin v.4 the way to disable Undo feature so changes are immediately saved is to add mutationMode="optimistic" to Edit/Create component.
To change the success notification message use mutationOptions={{ onSuccess }} together with notify hook
import { useNotify, useRefresh, useRedirect, Edit } from 'react-admin';
const notify = useNotify();
const refresh = useRefresh();
const redirect = useRedirect();
const onSuccess = () => {
notify(`Changes saved`);
redirect('/your-collection');
refresh();
};
return (
<Edit mutationMode="optimistic" mutationOptions={{ onSuccess }}>
...

Vuex how to handle api error notification?

I started working with Vuex 2 weeks ago and I realized that Vuex is very good to handle the state of the app. But, it is difficult to handle the error of API calls. When I get data from the server, I dispatch an action. When data is successfully returned, of course, everything is fine. But when an error happens, I change state, I don't know how to detect it through the state from Vuejs components to notify to the user. Could anyone give me some advice?
I typically have the following parts:
A component for displaying the notification, typically an alert or a snackbar or similar, e.g. error-notification. I use this component on a high level, directly below the root app component. This depends on your layout.
A property in vuex indicating the error state, typically an error object w/ error code & message, e.g. error
One mutation in the store for raising an error setting the error property, e.g. raiseError
One mutation in the store for dismissing an error clearing the error property, e.g. dismissError
Using these, you need to:
Display error-notification based on the error in the store: <error-notification v-if="$store.state.error :error="$store.state.error"/>
When an error occurs, call raiseError mutation (in your API callback): vm.$store.commit('raiseError', { code: 'ERR_FOO', msg: 'A foo error ocurred'})
In error-notification, call the dismissError mutation when the notification is closed.
You can also return a promise in your action so that if you call it from component you can catch the error there and display a notification as needed:
in your store:
//action
const fetch = (context) => {
return api.fetchTodos() //api here returns a promise. You can also do new Promise(...)
.then((response) => {
context.commit('SET_TODOS', response);
})
};
in vue component:
this.$store.dispatch('todos/fetch', modifiedTodo)
.catch(error => {
//show notification
})