prevent react-router from detecting location change - javascript

In my React/Redux based application, I have implemented logout like following
In reducers/index.js where I do combineReducers, I have created an app level reducer called appReducer. There I check for LOGOUT action and then return undefined.
All this works fine. What I want to do is, that for LOGOUT action, I also want to clear localStorage and redirect to login page. Please note that I want to redirect native browser way, not using react-router. If I do window.location = '/'. First its detected by react-router and I see login page for a bit and then it refreshes which is a bit odd.
Is there a way to prevent react-router from being notified on location change!?

You cannot prevent it completely but you can control it with the access to
history.listen function.
With React-Router 4 you can wrap top level components using the HOC withRouter.
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(App));
This allows accessing this.props.history and controlling it
class App extends Component {
constructor(props) {
super(props);
this.props.history.listen((location, action) => {
//here you can control the location change
});
}
render() {
return (
</div>
);
}
}
And you also have the listenBefore event on the history object and you can use this event to control the navigation and add your own custom navigation logic:
history.listenBefore( (location, done) => doSomething(location).then(done) )

Related

React-native navigation is it possible to intercept the back action?

Is it possible to intercept the back action in react native on any route?
Lets say I am on a page and I want navigate a user on a different route when he/she performs a back action.
or even a popup confirm.
You can use React's [BackHandler][1] for instance
// import the back handler from react-native
import { BackHandler } from 'react-native'
// fire the listener on every render (including the componentWillUnmount)
useLayoutEffect(() => {
BackHandler.addEventListener(
"hardwareBackPress",
{
console.warn('about to go back....')
}
);
}, [])
[1]: https://reactnative.dev/docs/backhandler

React: change url without rerender; using window.history?

I have a "settings" page in my react app. The page has several tabs rendering different parts of settings.
It would be better UX if a user can share urls with other users.
What I want is (inside "settings" page):
user A clicks a tab
url changes with a #tabname appended
user A send that url to user B, and user B open that url
user B sees the same tab as user A
But with react router, the whole page re-renders if the url changed:
import { withRouter } from "react-router-dom"
const MyComp = (props) => {
...
const onTabChange = () => {
// append #tabname here
props.history.replace(...); // or `push`
...
}
...
export default withRouter(MyComp)
}
After a lot of searches, I found a solution to use window.history:
const onTabChange = () => {
window.history.pushState(null, null, "#tabname");
...
}
This does the trick, but little information and explanation, and I'd love to know the consequences of using this trick.
Is this a valid solution (for a react app)? Will this cause any problem?
(PS. I know how to parse a url)
More details:
To be more specific, there is a AuthChecker wrapper for all pages. When react router's location changes, it checks for the route's allowed auths and current user's auth.
I've tried /path/:id and everything but all change location, so auth checked and page rerendered.
And I've given up a solution in react router and just want to know: is it safe to change url with window.history in a react app using react router to manage routes?
this question is already answerd at this post.
so it says window has a property called history and there is a method on history which helps you update the history state without react-router-dom understanding it.
like this:
window.history.replaceState(null, 'New Page Title', '/new_url');

how to know the value of flipToggle from another page using react native

I am new with react native , i have to pages which are homeScreen and profileScreen ,in profile screen there is a flipToggle and in the homeScreen there is a function that will excute if the flipToggle is on (i will use if statement) my question is when i am in homeScreen how to know if the flipToggle is turned on or off using react native
I think you're probably using React Navigation, so you can pass params to a route when you navigate to it, example:
navigation.navigate('HomeScreen', {
flipToggle: yourFlipToggleVariable // true or false
})
And in your HomeScreen you can retrieve this params using navigation.getParam, example:
// The second parameter is a default value
navigation.getParam('flipToggle', false)
But if you want to watch the flipToggle variable i recommend you to search about state mamagement with redux or context (React.createContext)
Some useful links:
https://reactnavigation.org/docs/navigation-prop/#navigate
https://reactjs.org/docs/context.html
https://redux.js.org/introduction/getting-started
https://redux.js.org/basics/example/

ReactJS one time initialisation

I have a Component say HomePage where I'm calling getCurrentLocation to get the location using GPS. This value will be used to select a value from a Drop Down. User may then use the dropdown to change the value.
When I navigate away from this page and then come back using
const appHistory = createHashHistory();
appHistory.goBack();
the constructor and the componentDidMount are executed again. So the user selected value is lost and i get the default value again.
So where do I put by initialisation code? I come from Ionic where there something like ionViewDidLoad which is executed only when the page loads the first time. Is there an equivalent for this is React.
Below is my code
export class HomePage extends React.Component {
constructor(props){
super(props);
state = {
currentLocationCoordinates:[],
};
this.getCurrentLocation = this.getCurrentLocation.bind(this);
}
getCurrentLocation(){
navigator.geolocation.getCurrentPosition((success)=>{
console.log("Current Location: "+success.coords);
this.setState({
currentLocationCoordinates:[success.coords.latitude,success.coords.longitude],
userLocationCoordinates:[success.coords.latitude,success.coords.longitude]
});
});
}
}
componentDidMount(){
this.getCurrentLocation();
}
}
I would avoid using componentDidMount for this. Make your components as resilient to re-renders and re-mounts as possible.
By the sounds of it you are after an application level state container that will hold your application state regardless of whether your component is mounted or not.
Most of the React community relies on Redux for this although other state containers do exist. I'd suggest having a look at Redux and using it to hold those location details as part of Redux store. They will then be always accessible on your component as props, regardless of whether it re-renders or not.
You need to use ComponentDidMount() together with a flag that detects whether the component has been initialized or not. You are on the right track.
componentDidMount(){
initialized ? "" : this.getCurrentLocation();
}

Component re-renders when using react-navigation for react native

I noticed that whenever I navigate to another page using the navigate props available to my component, it triggers a re-render of the component and componentDidMount is being called whenever I navigate to a screen that has rendered before.
For instance, when I navigate a user to their profile page and they decided to go back to the dashboard, the dashboard component which has been initially rendered is being rendered again and componentDidMount is being called thereby slowing down the application.
import { StackNavigator } from 'react-navigation';
const Routes = StackNavigator({
home: {
screen: HomeScreen
},
dashboard: {
screen: Dashboard
},
profile: {
screen: Profile
}
},
{
headerMode: 'none',
});
In my component I navigate the user with this.props.navigation.navigate('ScreenName')
I would appreciate any help to stop the component from re-rendering when navigating back to it. Thanks
I would have a state variable in your constructor that keeps track if you navigated. State is only relevant to the current component. So if you navigate to 'ScreenName' multiple times, the stack builds and each ScreenName component has its own state.
constructor(props)
super(props)
this.state = {
navigatedAway : false
}
Then before you navigate to your 'ScreenName' screen update the state
this.setState({
navigatedAway : true
},
() => {
this.props.navigation.navigate('ScreenName');
}
);
Use syntax above to make sure state isUpdated THEN navigate. Then like Dan said in comments above if your function shouldComponentUdate have a condition statement.
shouldComponentUpdate(newProps){
// return true if you want to update
// return false if you do not
}
* Side Note *
When you navigate I don't believe the component is unmounted. You could verify this by simply printing to console. Correct me if I am wrong though, I am fairly new to react native.
componentDidMount() {
console.log("COMPONENT_CONTENT_MOUNTED")
}
componentWillUnmount({
console.log("COMPONENT_CONTENT_UNMOUNTED")
}
If you are using React Navigation 5.X, just do the following:
import { useIsFocused } from '#react-navigation/native'
export default function App(){
const isFocused = useIsFocused()
useEffect(() => {
//Update the state you want to be updated
} , [isFocused])
}
If I understand your question correctly, when you navigate away, the component is unmounted.
When you navigate back, it must be re-mounted, hence re-rendered.
In general, any UI change necessitates a re-render. No way around that - It's kind of "by definition".
You might be able to cache the page.
Or use the reselect library to cache expensive to obtain data, so the calculations for re-rendering are quick and minimal.
If react/react-native thinks the props have changed (in an already mounted/rendered component), it will also re-render, but you can influence this decision via shouldComponentUpdate().
Just add React.memo to your export of component that reload each time.
So instead of
export default component
you would have:
export default React.memo(component);
Which does a comparison of props and only re-renders if props change (so not on navigate but on actual changes, which is what you want)

Categories