React Native StackNavigator: Refresh data after goBack() Action - javascript

I'm trying to re-render the first screen of the StackNavigator after saving the data for a new listItem. After a successful save, I call this.props.navigation.dispatch(NavigationActions.back())
and I want to re-render the page with a a this._updateData function. I tried a solution mentioned here: https://github.com/react-navigation/react-navigation/issues/922 by beausmith where the this._updateData function gets passed to this.props.navigation.navigate like so:
this.props.navigation.navigate('ScreenB', {
onNavigateBack: this._updateData
})
And then on ScreenB you would do this:
this.props.navigation.state.params.onNavigateBack()
And
this.props.navigation.dispatch(NavigationActions.back())
With a StackNavigator, you don't need to call this.props.navigation.navigate, as it is already built into the header. I tried adding this._updateData to this.props.navigation.state.params in the ComponentDidMount hook of screenA and then calling it as I had above in ScreenB but that didn't seem to do anything (No error message, but no refresh). Any idea where I'm going wrong?

Related

Invalid Hook call in React in function call

I'm quite new to react and get the following error:
Uncaught 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:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Situation:
in de index.js file I have the
ReactDOM.rend(,document.getElementByID('root'))
In this file, before the document.getElementByID, I route to all the different pages using react-router-dom. One of those pages contains a form, when this form is submitted I want to do something with the results of that form.
The form has an onSubmit to a function like:
const functionName = () => {
if (formID === "XXXXXX") {
<ComponentName/>
}
}
In this function I want to check if the ID of the form matches a certain ID, before calling another Component. Until this point it works perfectly, the function is called, it checks if the ID's are matching, but then:
What is the right way to call another component?
If I use componentName() I get this Invalid Hook call because the componentName file has some useState hooks. and if I use <componentName/> nothing happens the console logs wont show up in the console etc.
Note: In the componentName file, I just want to get the information that is used in the form, before passing it on to another file. so the componentName file doesnt have to render anything.
UPDATE: To load the forms in a page, there is a button: <Button fid={id} pageName='pageName'/>
In that Button file I can use hooks without a problem, the button has an onClick like: <button className={pageName} onClick={openTF}>buttonName</button> then that openTF function leads to another page where the form is shown and handled in a popup:
const openTF = () => {
Form(props.fid);
}
From that file on, I can't use any hooks
Update2:
I found out that the only works in the return part of a Component.
So i rewrote most of the code, and only problem I now have is that in that the return part is rendered everytime the useStates are updated. is there a way to make another AFTER all the useState. the way it is now the return part is called 4 times, and I only need it one time after all the UseStates
Does anyone know what I do wrong?

Prevent React Route to re-run componentDidMount() on traversing back on same route

For a given route localhost:3000/my-link, component MyLink is rendered.
In MyLink component, ajax request is fired in componentDidMount() which then sets the state object of the MyLink class and renders the component.
But when navigating to some other route and switching back to /my-link ajax is fired again. In short, whole state previously populated is lost. Can there be a way to check if the previous state is already populated, then prevent componentDidMount to be called?
componentDidMount() is invoked immediately everytime a component is mounted in the DOM. What you're trying to do can be achieved by integrating react redux store. Call the Ajax call according to store state variable. On firing the AJAX requests Dispatch the action to make the variable true.
componentDidMount() {
if(!this.props.variable)
{
//AJAX CALL
dispatch({ type: 'SET_VARIABLE', value: true });
}
}
Once it sets to true, it won't be called next time you navigate to this route.
When you switch from your existing route localhost:3000/my-link to any other page like localhost:3000/other-link, Your MyLink component got unmounted but at the same time, your data fetched in MyLink component is still in your redux store.
Here's what you can do;
If the data which you are fetching in MyLink is frequently changing then it is a good idea to fetch it again when you visit the same link again. Means, You need to clear the state when your component unmounts.
componentWillUnmount() {
// resetData is an action which will clear the data in your MyLink reducer.
this.resetData();
}
So, When you visit the same link again, The component won't have any previous data and it will fetch the fresh data.
If that data is not frequently changing and you're okay with getting it just one time then you can make a conditional call to your action.
componentDidMount() {
// this.props.data is a state from redux store.
if(!this.props.data){
this.getData();
}
}

Refresh / Reload ember route from a component

I have a component, that's actually a modal dialog.
When I am done with that dialog and press the "Ok" button, I want to stay on the stay page from where I opened that dialog.
Which isn't difficult.
But the problem is that the dialog changes the data (I am getting data through a REST call) so I need to refresh the route that I already am on to reflect the data changes.
Since, I am calling it from a component, I don't have Route so can't call route.refresh().
I tried to get the router:
this.set('router', Ember.getOwner(this).lookup('router:main'));
and did transition to the same page:
_this.get('router').transitionTo('my-route')
But since the route hasn't changed (I only opened a dialog), transitionTo doesn't get triggered!
Is there a way I can force trigger transitionTo or refresh the page that I am on?
Thank you!
First, you can easily get the current route name by injecting the routing service to the component.
Then, you can get the current route instance and apply its refresh method:
// app/components/a-component.js
import Component from "ember-component";
import service from "ember-service/inject";
import getOwner from "ember-owner/get";
export default Component.extend({
routing: service("-routing"),
actions: {
refresh() {
const currentRouteName = this.get("routing.currentRouteName");
const currentRouteInstance = getOwner(this).lookup(`route:${currentRouteName}`);
currentRouteInstance.refresh();
}
}
});
For this, define refreshCurrentRoute method in nearest route or in application route file.
actions:{
refreshCurrentRoute(){
this.refresh();
}
}
From your component, you need to call refreshCurrentRoute action. either you can use ember-route-action-helper or by passing the closure action.

React - prevent lifecycle components from rendering the same data twice: WillReceiveProps & componentDidMount

Quick React question for Components specifically. I am using two lifecycle methods:
componentDidMount() -for retrieving data when component is first rendered
componentWillReceiveProps(nextProps) - for updating data when some parameters change
This works great. However, when I refresh the page with the component, both lifecycle methods are executed when one will suffice. The data on page is still correct, however it seems a bit inefficient. How can I combine these to lifecycles if possible?
Below example will call fetchTest() twice when page is refreshed. If I remove componentDidMount, then the data will not initially load if user refreshes the page.
Any ideas on how to have fetchTest() called once no matter how the user gets to the component?
componentDidMount() {
fetchTest(this.props.params.id);
// for initial component render
}
componentWillReceiveProps(nextProps) {
fetchTest(nextProps.params.id);
// for when (params.id) is changed. data within component is updated
}
You probably want to do
componentWillReceiveProps(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
fetchTest(nextProps.params.id);
}
}

How to register page views in a React / Redux app

Edit
Actually, I'm fairly certain that this is because my action creator doesn't return an action and the first property of an action is supposed to be a type and that is therefore undefined...
I have a React/Redux SPA that I want to register page views on with a custom analytics engine (ie, not Google Analytics). I'm trying to register page views.
So I have attempted to do this by setting lifecycle hooks in React to fire a Redux action:
class ConfirmationPage extends Component {
...
componentWillMount() {
this.props.registerPageVisited('confirmation');
}
}
However, I receive a Cannot read property 'type' of undefined error presumably because I am modifying the state via the props. Looking at the stack trace, it brings me to that hook. However, I've tried other hooks such as componentDidMount and even componentWillUnMount and I get the same error.
For context, my action creator is this:
export function registerPageVisited(page) {
DB.child('visits')
.child(store.getState().visit)
.update({ [page]: true });
}
where the DB is a firebase reference.
So, how should I keep track of page views?
Could you provide code where you instantiate ConfirmationPage component?
In the componentWillMount function what you are actually doing is calling registerPageVisited function which should be passed as props which should look something like: <ComponentPage registerPageVisited={registerPageVisited} />
As you are suspecting, since your registerPageVisited does not return action object it is not action creator. If you will make it an action creator, then you should use mapDispatchToProps function to use it like any other props as sen in your example.
An alternate to this is have a different object listen to notifications from the store and update the DB based on the changes it's seeing. That will isolate any metrics collection from your view hierarchy.
You'd have to replicate some logic that determines which React components are getting shown into that object, but you could pull that into a separate class and use it in both places.
If you're using react-router, there might be hooks that fire when the active route changes. However, I haven't investigated that.

Categories

Resources