Submitting Formik form in React Native - 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.

Related

React Native Navigation: Navigate to a screen from outside a React component

https://wix.github.io/react-native-navigation/docs/basic-navigation#navigating-in-a-stack
indicates that pushing a new screen to the stack requires the current screen's componentId. My use case is to allow navigating based on certain events emitted by a native module. As such, componentId won't be available to me, since the event listener would reside outside any React component screen. Is there any way to navigate to a screen from outside in RNN? Or even get the current componentId.
I ended up adding a command event listener to store the current componentId in closure.
let currentComponentId;
Navigation.events().registerCommandListener((name, params) => {
if (name === 'push') {
currentComponentId = params.componentId;
}
});
Navigation.events().registerAppLaunchedListener(() => {
Navigation.setRoot(rootRouteConfig);
EventEmitter.addListener('navigate', (name) => {
Navigation.push(currentComponentId, {
component: {
name,
},
});
});
});

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.

Handling 401 responses in a react-native functional component

I'm getting the following warning when an API request returns 401 and I send the user to the login screen:
Warning: Can't perform a React state update on an unmounted component.
What is the best way to handle this warning in a functional component that uses hooks. See the code below:
.
.
export default function MovieDetailsScreen() {
const [movie, setMovie] = useState({});
const movieId = useNavigationParam('movieId');
useEffect(() => {
// This is the method that does the request and returns 401 (It
// uses the fetch library)
Client.movies.show(movieId)
.then(result => {
setMovie(result)
})
}, [])
.
.
.
In general, warnings don't crash your application. But you should care about them. For instance, the previous warning(s) can lead to performance issues when not properly unmounting your stateful components
the request (e.g. Promise) isn't resolved yet, but you unmount the component. Then the request resolves, setMovie() is called to set the new state, but it hits an unmounted component.
You can try catch
Client.movies.show(movieId)
.then(result => {
setMovie(result)
})
.catch((err) => {
Client.movies.stop()
})

Handle Vue render errors locally

I am using Vue (server side rendered) with mjml to generate emails.
So I have something (overly simplified) like:
<mjml><mj-body>Hello {{ User.Name }}</mj-body></mjml>
If the model doesn't define User then Vue throws an error and the whole output is lost.
What I want to the output to be along the lines:
<mjml><mj-body>Hello <error>'User' is undefined</error></mj-body></mjml>
I have implemented Vue.config.errorHandler but that just tells me about the error -- there is no rendered output.
Anyway to implement the equivalent of an error handler around each variable substitution?
If you are using Vue version >= 2.5, you can use errorCaptured to create an ErrorBoundary
const ErrorBoundary = {
name: 'ErrorBoundary',
data: () => ({
error: false,
msg: ''
}),
errorCaptured (err, vm, info) {
this.error = true
this.msg = `${err.stack}\n\nfound in ${info} of component`
},
render (h) {
return this.error
? h('pre', this.msg)
: this.$slots.default[0]
}
}
and use this in your component
<error-boundary>
<mjml><mj-body>Hello {{ User.Name }}</mj-body></mjml>
</error-boundary>
If the application has any javascript error, it will be displayed on UI
Example on codepen
If you want to have more user-friendly error, you can customize ErrorBoundary to have fallback to another component. You can find out in this tutorial
Another good example of using errorHandler

React Native startup optimization

I am looking for a way to optimize the startup time of a pure react native mobile app.
As a JavaScript framework, is that possible to bundle the JavaScript files into separated files, say something like common.js and app.js. I was searching via Google with keywords something like "react native webpack" stuff but it seems like all these libraries are deprecated or out of date, such as react-native-webpack-server, react-native-webpack-starter-kit etc.
I am wondering if anybody here is also looking for a way to optimize the JavaScript bundle in react native. Or, maybe these third party bundle approach has been overcame by Facebook standard bundle?
You could dynamically load your component, in this way your bundle.js will contain only the fraction of js needed and as you navigate you will request the other different parts / fractions.
Rather than do the traditional way: import App from './containers/App/App'; you could do something like this:
class ImportedComponent extends Component {
state = {
component: null
}
componentWillMount() {
this.props.load()
.then((mod) => this.setState(() => ({
component: mod.default
})))
}
render() {
return this.props.children(this.state.component)
}
}
const App = (props) => (
<ImportedComponent load={() => import('./containers/App/App')}>
{(Component) => Component === null ? <h6 className="loading-message">Loading...</h6> : <Component {...props}/>}
</ImportedComponent>
)
or you can lazy load your component itself. Let's say for example that I have Moment JS and I don't want to load it until it's needed. so what I could do:
1) Create a state and set it to null.
constructor(props){
super(props);
this.state = {
lazyLoadedComponent: () => null
}
}
2) Use async componentDidMount with await, try and catch and update the state lazyLoadedComponent on componentDidMount
async componentDidMount(){
try {
const Moment = await import('react-moment');
this.setState({ lazyLoadedComponent: (data)=>{
return React.createElement(Moment.default, {format:'MM/DD/YY'}, data)
}
});
}
catch(err) {
this.setState({ lazyLoadedComponent: <div>{`Failed to load component: ${err}`}</div> });
}
}
3) Call the component on the render:
{this.state.lazyLoadedComponent(value.createdOn)}
By following these 2 examples you should, hopefully, be looking at a bundle.js under 250KB.
As a possible solution you can use ram-bundle format, that metro bundler provides.
In this case you will not load the entire js-bundle - you will load only part, that you need at a startup (in a lot of application are a lot of places, which user may not even see, and this feature allow you load such parts, only when they are required). So you can simplify your entry point and load only small piece of your bundle.
You can look at react-native-bundle-splitter. This library well integrated with almost all popular navigation libraries and allows you to postpone a loading of specific routes. For example, if you have a login screen, you can load at start up only this screen, and all others load in background or start the loading of them, only when user can see them.