Unmount and mount between components in React-router v4.2 - react-router-v4

I'm using react-router v 4.2 and meet some problems. There are two components:
First one is:
class One extends React.Component {
componentWillMount() {
console.log('One mount');
}
componentWillUnmount() {
console.log('One unmount');
}
render() {
...
}
}
Second one is:
class Two extends React.Component {
componentWillMount() {
console.log('Two mount');
}
componentWillUnmount() {
console.log('Two unmount');
}
render() {
...
}
}
When I turn the page from component One to Two. And return to One. I would get:
One mount >
One unmount >
Two mount
> One mount >
Two unmount
The One was mounted first then Two unmounted. It's strange! Then, I tried to change the react-router to v3.0.0 then the problem was gone! So I think it was the problem in v4.2

I solved this problem. Thanks for Ernie Yang in front-end community of Facebook: Link
It simply added Switch component into route like:
<Switch>
<Route path="/Two" component={Two} />
<Route path="/One" component={One} />
</Switch>
The codepen created by Ernie Yang:
https://codepen.io/anon/pen/jaEKve?editors=1111

Related

React Native components seem to be sharing a state

I'm having an issue with React-native where I have a component TouchTimer which uses an AnimatedTimer component. This timer is supposed to start and stop when it is tapped, which it does, however all of the TouchTimer components I add to a page will start and stop whenever any of them are tapped, rather than only affecting the tapped component.
Below is a snippet of my component:
TouchTimer.tsx
export class TouchTimer extends React.Component<TouchTimerProps> {
state: {
...
paused: boolean,
}
constructor(props) {
super(props);
...
this.state = {
...
paused: true,
}
}
startStop() {
this.setState({paused: !this.state.paused});
}
render() {
const { time } = this.props;
return (
<TouchableHighlight onPress={() => this.startStop()}>
<View>
<AnimatedTimer
...
time={time}
pause={this.state.paused}
/>
<View style={styles.timeContainer}>
<Text style={styles.time}>{this.state.remaining}</Text>
</View>
</View>
</TouchableHighlight>
)
}
}
And here is a snippet of the screen containing these components:
Details.tsx
import { TouchTimer } from '../components/TouchTimer';
...
export class RecipeDetailsScreen extends React.Component<NavigationInjectedProps> {
...
{this.state.steps.map(step => (
<List.Item
key={step.id}
title={"Step " + step.index}
style={styles.step}
description={step.short_desc}
right={() => (step.time > 0 &&
<TouchTimer
time={step.time * 60000}
/>
)}
/>
)
}
I have tried wrapping the TouchTimer components in a View and changing the paused boolean to a prop, to no avail.
I have also tested to see if this issue appears when the components are not siblings, and when they are not produced as the result of a callback, and the issue still persists in both these cases.
If anybody has any advice or answers on how to make these timers independent I would very much appreciate it!
Curiously that component seems to be implemented with a global pauseFlag that applies to all component instances. See https://github.com/dalisalvador/react-native-animated-timer/blob/master/src/Components/AnimatedTimer.js#L34
So I don't think you're doing anything wrong here, this is a limitation of the library code that is coupling all instances of your timer to the same pauseFlag value.

Using React BrowserRouter (v4) how do I recognize when the location has changed?

Have the following JSX
// Root.jsx
<BrowserRouter getUserConfirmation={this.handleLocationChange}>
<Switch>
<Route path='/' exact component={Search} />
<Route path='/queue/' component={Queue} />
<Route path='/healthCheck/' component={HealthCheck} />
<Route path='/transcript' component={Transcript} />
</Switch>
</BrowserRouter>
// Root.js
export class Root extends Component{
constructor(props){
super(props);
}
handleLocationChange(message, callback) {
// your staff here
console.log(`- - - location:`);
callback(true);
}
render(){
return RootTemplate.call(this);
}
}
But when I run this I get...
Root.jsx:25 Uncaught TypeError: Cannot read property 'handleLocationChange' of undefined
at RootTemplate (Root.jsx:25)
at Root.render (Root.js:13)
If I try this...
getUserConfirmation={()=>this.handleLocationChange()}
I don't get an error but I also don't get my console like I would expect.
How do I tell when the location has changed?
Update
I also tried this just for testing...
const getConfirmation = (message, callback) => {
console.log("Welp this is working");
callback(true)
};
...
<BrowserRouter getUserConfirmation={getConfirmation}>
But still not seeing anything in the log.
If your trying to check when a user has navigated from one location in your app to another you should use the history library. You can also use it for getUserConfirmation, which is when a user navigates away from your application.
import createHistory from "history/createBrowserHistory";
import { Router } from "react-router";
// Fires when user navigates away
const getUserConfirmation = () => console.log("User navigated away");
// Create your browser history
const history = createHistory({getUserConfirmation});
// Fires every time a location is updated
history.listen((location) => console.log("Navigated to page", location));
// Attach your browser history to your router
const MyRouter = () => <Router history={history> {your children} </Router>
Use Prompt component from react-router. Include in one of you components.
Try doing this without using getUserConfirmation.
Prompt- https://reacttraining.com/react-router/core/api/Prompt

React Native Navigator: Can I remove navigation gestures from a scene after a user action?

I want to disable the swipe from left pop gesture on the navigator after the side menu has been accessed within a scene. I don't want to disable it when the scene first renders, only when the side menu is open. I have an onOpen function I can call, but I don't know how to programatically change the navigation gestures without pushing another route.
I tried setting the configureScene prop of the navigator like this:
configureScene={() => {
return this.state.swipeBackNavigation ? FloatFromRight : Navigator.SceneConfigs.FloatFromRight;
}
and changing the state, but the component doesn't rerender
ideas would be appreciated
I believe you can just set gestures to null (effectively disabling it):
gestures: {}
I can't test this currently, but I suspect it will work (if I didn't screw up some syntax somewhere):
export default class Foo extends Component {
constructor(props){
super(props);
this.state = {
//initialize gestureChoice
gestureChoice: {},
}
}
disablePop(){
setState({ gestureChoice:{ gestures:{} } });
}
enablePop(){
setState({gestureChoice: ...Navigator.SceneConfigs.FloatFromRight});
}
render(){
return(
<Navigator
renderScene={(route, navigator) =>
return <SomeScene navigator={navigator} {...route.passprops} />
}
configureScene={(route, routeStack) =>
this.state.gestureChoice;
)}
/>
);
}
}
The idea being, you could use enablePop() and disablePop() whenever you would like.
This thread is probably helpful: https://github.com/facebook/react-native/issues/1014

react-native-router-flux: Navbar buttons need to access scene component methods

I am inserting a rightButton into react-native-router-flux using the renderRightButton api. The problem I ran into is that the rightButton needs to access a method within the component.
class App extends React.Component {
render() {
return(
<Router>
<Scene
key="firstScene"
component={FirstScene}
title="First scene"
rightTitle="Apply"
renderRightButton={this.getRightButton}
/>
</Router>
)
}
getRightButton() {
return(
// someMethodOnFirstSceneComponent lives on the FirstScene component
<View onPress{this.someMethodOnFirstSceneComponent}>
<Text>
Invoke Function from Scene Component
</Text>
</View>
)
}
};
One option would be to put all of the data currently in the state of FirstScene into a redux reducer and then put the someMethodOnFirstSceneComponent on the FirstScene component. However, I am trying to keep state within components and not rely on reducers because from my experience, it keeps things easier to reason about.
I've found a way to do that. Not a very nice solution, but it works. I'm setting onRight method in the component constructor
constructor(props) {
super(props);
this.props.component.onRight = () => {
console.warn("call from my onRight function")}
}
}

React native Android Home always starts a splash screen

Using navigator I hit home from my react native android app and then return to the app and it always starts at the initial route which is my splash screen. Is there away to keep the component that was in view last the component that will be in view when the app re-opens?
class AwesomeProject extends Component {
render() {
return (
<Navigator
style={{ flex:1 }}
initialRoute={{ id: 'SplashPage' }}
renderScene={ this.renderScene }
/>
);
}
renderScene(route, navigator) {
if (route.id === 'SplashPage') {
return (
<SplashPage
navigator={navigator} {...route.passProps}
/>
);
}else if(route.id === 'HomePage'){
return (
<HomePage
navigator={navigator} {...route.passProps}
/>
);
}else if(route.id === 'ListViewPage'){
return (
<ListViewPage
navigator={navigator} {...route.passProps}
/>
);
}
}
}
You need to add the following code to /android/app/src/main/java/com//MainActivity.java. so, that it maintains the stacks of application.
#Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
if you dont achieve your answer from above please follow reference as below:
https://facebook.github.io/react-native/docs/native-modules-android.html
and in last of above reference there is lifecycle event for application in react-native. So, follow same strategy like native android onPause/onDestroy/onStop method and please solve your problem.
can you make sure you app is live when you press home?
My first suggestion is not to use splashscreen as a scene. If you use that in such a way it is not splashscreen rather a component. Use the native java code as mentioned in the links below to get your problem solved like charm.
https://github.com/react-native-component/react-native-smart-splash-screen
with this there are certain import errors which can be fixed looking at the issues section or check my code in github which contains the perfect use of the splashscreen module.
https://github.com/UjjwalNepal/Dental
Hope this helps.
Thank you