How to navigate in React-Native? - react-router

I am using ReactNavigation library in my react-native project and since 6 hours I am trying to navigate from one screen to others screen and have tried every possible way but I think I am not able to get the logic properly.
This is my project structure.
Here
The way I am doing it.
const AppStack = StackNavigator({ Main: Feeds });
const AuthStack = StackNavigator({ Launch: LaunchScreen, });
export default SwitchNavigator({
Auth: AuthStack,
App: AppStack
});
In my LaunchScreen.js
const SimpleTabs = TabNavigator(
{
Login: {
screen: Login,
path: ""
},
SignUp: {
screen: SignUp,
path: "doctor"
}
},
);
<SimpleTabs screenProps={{rootNavigation : this.props.navigation }}/>
But the problem is in my LaunchScreen Component there is a TabNavigator which contains my other two components Login.js and SignUp.js but the button in my Login.js doesn't navigate it to Feed.js.
When you click on the button this is performed.
signInAsync = async () => {
await AsyncStorage.setItem('userToken', 'abc');
this.props.navigation.navigate('Main');
console.log("AAAAAsSSS");
};
My LaunchScreen.js contains a TabNavigation which lets you slide between two components ie. Login.js and SignUp.js.
Now when you click on the Login button which is in Login.js component it will authenticate the user and will switch the entire LauchScreen.js component with the Feed.js component.
I am a noob to react-native.

You can use react-native-router-flux (npm install --save react-native-router-flux)
just make one Navigator.js file and define each page you wanted to navigate.
import React from 'react';
import { Router, Scene } from 'react-native-router-flux';
import LaunchScreen from '../components/LaunchScreen.js';
import Feed from '../components/Feed.js';
const Navigator = () => {
return (
<Router>
<Scene key="root">
<Scene key="lauchscreen" component={LaunchScreen} hideNavBar initial />
<Scene key="feedscreen" type="reset" hideNavBar component={Feed} />
</Scene>
</Router>
);
};
export default Navigator;
now in your App.js file add this:
import Navigator from './src/Navigator.js';
export default class App extends Component<Props> {
render() {
return (
<Navigator />
);
}
}
now in your login.js when you click on login button write this:
import { Actions } from 'react-native-router-flux';
onLoginClick() {
Actions.feedscreen();
}
Thats it.. happy coding.

If you want to navigate to Feeds.js then navigate as
this.props.navigation.navigate('App');
not as
this.props.navigation.navigate('Main');
because your
export default SwitchNavigator({
Auth: AuthStack,
App: AppStack // here is your stack of Main
});
refer example

I came across the same issue few months ago. Thank god you have spent just 6 hours, i almost spent around 4 days in finding a solution for it.
Coming to the issue, Please note that in react-navigation you can either navigate to siblings or children classes.
So here, You have a swtichNavigator which contain 2 stack navigators (say stack 1 and stack 2), stack1 has feeds and stack2 has a tab navigator with login and signup.
Now you want to navigate from login.js to feeds.js(say file name is feeds.js). As mentioned already you can not navigate back to parent or grandparent. Then how to solve this issue?
In react native you have the privilege to pass params (screenprops) from parent to children. Using this, you need to store this.props.navigation of launchScreen into a variable and pass it to tab/login (check the tree structure). Now in the login.js use this variable to navigate.
You are simply passing the navigating privilege from parent to children.
Editing here:
<InnerTab screenProps={{rootNavigation : this.props.navigation }} />
Here, InnerTab is the tab navigator.
export const InnerTab = TabNavigator({
login: {
screen: login,
},
},
signup: {
screen: signup,
},
},
},
in login class, use const { navigate } = this.props.screenProps.rootNavigation;
Now you can use variable navigate.
I know its little tricky to understand but i have tried and it works.

Write your Navigator.js file as below,
import React from 'react'
import { NavigationContainer, useNavigation } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
const SwitchNavigatorStack = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='{nameofscreen}' screenOptions={screenOptions}>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
<Stack.Screen name='{nameofscreen}' component={{nameofscreen}}/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default SwitchNavigatorStack
Once, you are done with that change your App.js file to,
import SignedInStack from './navigation'
import React from 'react'
export default function App() {
return <SwitchNavigatorStack/>
}
After this, you are done with setting your project for navigating. In all the components where you want to add navigation feature make sure you use the navigation.navigate() (or) navigation.push() method. Also make sure you hook navigation constant by import useNavigation library. For example,
const Login = () => {
const navigation = useNavigation()
< Button title = 'Login' onPress={() => navigation.navigate('{nameofscreen}')} />
}
with this code snippet you can implement navigation between screens using #react-navigation/native and #react-navigation/stack

Related

I am displaying dynamic data on this React Boostrap Slider. Is there a way to make the code more efficient?

this is my first post here. I am building a React Boostrap Carousel that pulls Movie data from the database and displays it. I am new to React and programming in general. So far i made the code work. But i do not know how to handle the images. The images are stores in React **src/assets/imgs. **. Should i store a reference to the image in the database like so ../../assets/imgs/the-batman.jpg and then display it? If so later on on the project the admin will have to create a MovieOfTheMonth. He should be able to input movie title, descrition etc, and also upload a movie image. Is there a way when the image is uploaded it, to store it to a specific folder, in this case src/assets/imgs and also create a reference in the database? I do not need the solution here, just to tell me if it is achievable. Finally is there a way to improve my code?
this is my full code for this component
import React, {useState, useEffect } from 'react';
import './Carousel.css'
import Carousel from 'react-bootstrap/Carousel';
import 'bootstrap/dist/css/bootstrap.min.css';
import axios from 'axios';
const CarouselHero = () => {
//boostrap code
const [index, setIndex] = useState(0);
const handleSelect = (selectedIndex, e) => {
setIndex(selectedIndex);
};
//Get Movies of the month
const [movie, setMovie] = useState([])
const getMovie = () => {
axios.get("http://localhost:4000/moviesOfTheMonth")
.then((res) => {
const myMovie = res.data
myMovie.push()
setMovie(myMovie);
})
}
useEffect(() => getMovie(), []);
return (
<Carousel activeIndex={index} onSelect={handleSelect} fade>
{movie.map((item) => {
const {id, title, description} = item.Movie
return (
<Carousel.Item interval={2000}>
<img
src={require("../../assets/imgs/the-batman.jpg")}
alt="First slide"
/>
<Carousel.Caption >
<h1>{title}</h1>
<p>{description}</p>
<button>Book Now</button>
</Carousel.Caption>
</Carousel.Item>
)
})}
</Carousel>
);
};
export default CarouselHero;
I think technically it is achievable to iterate over the assets folder and create database entries for new images (create and compare hash?), but it is usually not how you do it. I would put images in some file storage like S3 and reference them with id.
I don't know who the admin will be in your project, but if admin is rather a non technical person, you could create (or use a template of course) a small and simple admin dashboard, where he/she can maintain a movie of the month via UI.
FFinally some remarks on your code:
const handleSelect = (selectedIndex, e) => { setIndex(selectedIndex); }; - If you need only first, but not second, third etc. argument, you can just leave it out: (selectedIndex) => ...
const [movie, setMovie] = useState([]) - don't forget to use semicolon after every statement. (They are optional, but are useful sometimes to avoid weird errors). Also, you have a list here. So maybe better call it "movies".
myMovie.push() - What are you trying to push here?
useEffect(() => getMovie(), []); - Usually you define and call async function directly in useEffect. Don't you get any hints or warning?
movie.map((item) => { - When you iterate and get a list back React needs a key on every element (here on Carousel.Item). Don't just use the index, as it is a bad practice. Always try to find id property in your data.
const {id, title, description} = item.Movie - Why is the data nested by Movie object? Can't you just say item.id, item.title, item.description?

In React application, Images cannot show when an image url is string property of a object

I cannot show the image when the image url is string property in a object.
For example:
import React, {Fragment, useState} from 'react';
const profile = (props) => {
const [user, setUser] = useState(null);
useEffect(() => {
setUser({
name: 'John',
photo: './asset/images/user.png'
})
}, [])
return (
<Fragment>
<img src={user.photo} alt="photo" />
<p>{user.name}</p>
</Fragment>
)
}
Of course I can solve this by importing image object like import img from './assets/images/user.png'. But I need to know how to show image without importing it.
Does anyone solve the problem?
You can see one example working here: https://codesandbox.io/s/relaxed-sinoussi-ikown?file=/src/App.js
The main problem is you're trying to access the image as a relative route from your component.
But, when you want to do something like that your component can be anywhere in the application. So, the way to do it is:
Include your images in public/
Access your images using the absolute route from /public

What is ACTUALLY the right way to transition/redirect using React Router?

I'm in the process of learning React Router, and I can't seem to figure out what is currently the recommended way to redirect. I've read many articles and StackOverflow posts, but there is so much variety that I'm unsure of which one to implement for the current version of React Router.
What I'm trying to do is to redirect after an AJAX call using axios.
axios.post('/some-route')
.then(/*I want to redirect here*/)
Note: this code lives inside a function, inside a React.Component, that is called upon the submission of a form.
How do I accomplish this?
Please kindly let me know what details you need.
You can use browserHistory :
import { browserHistory } from "react-router";
browserHistory.push({
pathname: '/your/path'
});
This answer is for react-router-v4.
If you want to redirect from the same component (not from some action) and this component is rendered by some route then you can use history object passed in the props.
componentDidMount(){
if(this.state.some_param){
this.props.history.push("/some_location")
}
}
Best way is to create your own history object. You can use this history object in any action.
//history.js
import createHistory from 'history/createBrowserHistory'
export default createHistory()
then you can use this history in your router,
import history from "./history"
<Router history = {history}>
//other code
</Router>
now you can use this history object anywhere for redirect,
axios.post('/some-route')
.then(res=>{
history.push("/some_location")
})
const {Component} = React
const {render} = ReactDOM
const {Router, Link, Route, Switch, withRouter, Redirect} = ReactRouterDOM
const createHistory = History.createHashHistory
const myhistory = createHistory()
class App extends Component{
redirectToHome(){
myhistory.push("/home")
}
redirectToAbout(){
myhistory.push("/about")
}
render(){
return(
<div>
<Route path = "/home" component = {Home} />
<Route path = "/about" component = {About} />
<button onClick = {this.redirectToHome}>Redirect to home</button>
<button onClick = {this.redirectToAbout}>Redirect to about</button>
</div>
)
}
}
const Home = ()=>{
return(
<div>
Home
</div>
)
}
const About = ()=>{
return(
<div>About</div>
)
}
render(<Router history = {myhistory}><App/></Router>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<script src="https://unpkg.com/react-router-dom#4.1.1/umd/react-router-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/history/4.6.3/history.min.js"></script>
<div id="app"></div>
Starting from React Router v4, browserHistory is no longer exported from react-router.
You have two possibilities.
1) If your component is rendered via a route (is the component prop of a <Route> component, then you automatically get a few objects as props:
history
match
location
You can then use this.props.history.push("/some_location") in the context of the component
2) If your component is not related to a specific route, you can get the same props by using the withRouter high order component, which is part of react-router
import { withRouter } from "react-router-dom";
const Component = ( { history, location, match } ) => (
// your component code
);
export default withRouter(Component);

How can I reset redux state when hitting go back in a web browser?

This is my use case:
In the WelcomeScreen I have code like this:
class WelcomeScreen extends Component {
render() {
const {
checkoutState,
} = this.props;
if (checkoutState.status === TRYING_TO_BUY) {
return this.renderPurchaseForm(plan);
}
return this.renderWelcome();
}
When the user visit /welcome, he can hit a purchase button, which will dispatch an action and set the checkoutState.status to TRYING_TO_BUY. The Welcome will be rerendered by calling renderPurchaseForm
Within renderPurchaseForm, it will render a ArticlePurchaseBlock
renderPurchaseForm() {
const { articleId } = this.props;
return (
<ArticlePurchaseBlock
articleId={articleId}
/>
)
and in the block, the class will try to update the url to reflect that it is in an input form
import { withRouter } from 'react-router';
class ArticlePurchaseBlock extends Component {
componentWillMount() {
const { history } = this.props;
history.push(URL_BUY_ARTICLE);
}
render() {
// render a redux-form
}
}
export default withRouter(ArticlePurchaseBlock);
You can see the history.push(URL_BUY_ARTICLE); is called in componentWillMount.
Now the problem is: when the user in the purchase form, if a user wants to go back to previous url (/welcome) , he can't. It is because the state of checkoutState.status is still TRYING_TO_BUY. The welcome is always rendered to the form.
Is there any where within the ArticlePurchaseBlock I can monitor the go back event and unset the state? I do not plan to use redux-saga-router yet because of time constraint.
I am using react-router v4
I designed a router for this exact problem. It's excessively difficult with react-router. https://github.com/cellog/react-redux-saga-router. For your code:
https://gist.github.com/cellog/0731f7e1ba8f9009f6b208c2bd15aa16
The entire thing can be done in 1 line of code, and your routes look almost identical to react-router, with 1 additional line for mapping param or url change to action.

Store HTML page in memory with Angular2

I would like to keep web page in memory so that when I click on back button (not the one on web browser) or on a routerlink, the HTML page instantly loads if I already visit it(because I have some data to load that I don't want to be reload).
I've seen a method with tabbed interface : https://www.w3.org/Style/Examples/007/target.en.html#tab1
but it is not adapted for my code architecture.
I'm using routerlinkin angular2 to navigate threw pages and a back button calling a function on click to go to the previous page.
I try to detail as far as I can so people can understand better my code architecture and the way routerlink method works.
Back button function (works independently from routerlink) :
goBack() {
window.history.back();
}
The router link method from page 1 to page 2:
page1.html :
<a[routerLink]="['PAGE2']"> go to page 2</a>
page1.ts component:
import { Router, ROUTER_DIRECTIVES } from '#angular/router-deprecated';
#Component({
selector: 'page1',
templateUrl: 'page1.html',
styleUrls: ['page1.css'],
directives: [ROUTER_DIRECTIVES]
})
main.ts :
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '#angular/router-deprecated';
#Component({
providers: [ROUTER_PROVIDERS]
})
#RouteConfig([
{ path: '/page2', name: 'PAGE2', component: Page2}]) //component representing class Page2 in page2.ts
Any idea to help me manage it is welcomed, thanks by advance !
Just cache the data in the service like explained in What is the correct way to share the result of an Angular 2 Http network call in RxJs 5?
There is currently no way to prevent the router from re-creating the component when you route away and back to the component.
Well, for those having the same problem, I think the best way to manage it is to map the data into a localStorage key like this :
localStorage.setItem('favoris',JSON.stringify(my_array)); //set my data array
array = JSON.parse(localStorage.getItem('key_name')); //get in array
And then ngOnInitin the class called by the router will call the initial function depending of localStorage key being true or not.