React Navigation - Pass props into Navigator - javascript

My React Native app has several conditions in which it needs to handle deep links when the user clicks on a link for an invite. My intent was to setup the Link listeners in the main app.js file and when a deep link is detected I need to pass the data into the Splash view to determine several factors before adding the user to the group they are invited to.
I am using React Navigation. I initially tried to use redux by updating the store with the url data but ran into timing issues as the Splash page was loading before redux was updated. So I have attempted to create an HOC wrapper for the Navigator as follows:
HOC Linker:
import * as React from "react";
const Linker = (Component) => {
return ({ children, ...props }) => {
return (
<Component { ...props }>
{ children }
</Component>
);
}
};
export default Linker;
In my App JS file I import the Linker and wrap the main navigator with the HOC component. I then add a prop that is updated when React Native Linking method detects a deep link. It updates state with the parameters.
App JS:
import Navigator from './ui/navigation/navigator';
const LinkerWrapper = Linker(Navigator);
render() {
return (
<LinkerWrapper linking={this.state.url} />
);
}
My Navigator file looks as follows:
Navigator:
import { createSwitchNavigator } from 'react-navigation';
import Splash from '../screens/splash/splash';
import AuthStack from './auth';
import AppStack from './app';
const Navigator = createSwitchNavigator({
Loading: Splash,
Auth: AuthStack,
App: AppStack
});
export default Navigator;
When I click on a deep link the app opens as expected, I parse the url and prep the data object, state is updated and the splash page is re rendered as expected, the props are passed through the HOC, but when the componentWillReceiveProps method is ran in the Splash page I do not see any props from the linking prop.
I have tried many different things. I am really new to react so I know I am probably doing something fundamentally wrong. I am guessing I am missing something with React Navigation. One of the things I tried was to change the screen value to a function passing in the props. I was hoping that would magically make the linking prop appear in the Splash page props. Below was my attempt:
import React from 'react';
import { createSwitchNavigator } from 'react-navigation';
import Splash from '../screens/splash/splash';
import AuthStack from './auth';
import AppStack from './app';
const Navigator = createSwitchNavigator({
Loading: {
screen: props => <Splash { ...props }/>
},
Auth: AuthStack,
App: AppStack
});
export default Navigator;
Unfortunately that didn't work. Any assistance would be appreciated.
Thanks.

Related

How to have the same component on different routes in next js? [duplicate]

Trying next with layout pattern:
https://github.com/zeit/next.js/tree/canary/examples/layout-component
And the problem is that Layout component get remounted on every page change. I need to use layout component as a Container so it'll fetch data from server on every mount. How can I prevent layout to get re-mounted? Or am I missing something there?
This helped me for persistent layouts. The author puts together a function that wraps your page components in your Layout component and then passes that fetch function to your _app.js. This way the _app.js is actually the components that renders the Layout but you get to specify which pages use which layout (in case you have multiple layouts).
So you have the flexibility of having multiple layouts throughout your site but those pages that share the same layout will actually share the same layout component and it will not have to be remounted on navigation.
Here is the link to the full article
Persistent Layout Patterns in Next.js
Here are the important code snippets. A page and then _app.js
// /pages/account-settings/basic-information.js
import SiteLayout from '../../components/SiteLayout'
import AccountSettingsLayout from '../../components/AccountSettingsLayout'
const AccountSettingsBasicInformation = () => (
<div>{/* ... */}</div>
)
AccountSettingsBasicInformation.getLayout = page => (
<SiteLayout>
<AccountSettingsLayout>{page}</AccountSettingsLayout>
</SiteLayout>
)
export default AccountSettingsBasicInformation
// /pages/_app.js
import React from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps, router } = this.props
const getLayout = Component.getLayout || (page => page)
return getLayout(<Component {...pageProps}></Component>)
}
}
export default MyApp
If you put your Layout component inside page component it will be re-remounted on page navigation (page switch).
You can wrap your page component with your Layout component inside _app.js, it should prevent it from re-mounting.
Something like this:
// _app.js
import Layout from '../components/Layout';
class MyApp extends App {
static async getInitialProps(appContext) {
const appProps = await App.getInitialProps(appContext);
return {
...appProps,
};
}
render() {
const { Component, pageProps } = this.props;
return (
<Layout>
<Component {...pageProps} />
<Layout />
);
}
}
export default MyApp;
Also, make sure you replace all the to <Link href=""></Link>, notice that only have change the Html tag to link.
I struggled because with this for many days, although I was doing everything else correctly, these <a> tags were the culprit that was causing the _app.js remount on page change
Even though this is the topic Layout being mounted again and again, the root cause of this problem is that you have some data loaded in some child component which is getting fetched again and again.
After some fooling around, I found none of these problem is actually what Next.Js or SWR solves. The question, back to square one, is how to streamline a single copy of data to some child component.
Context
Use context as a example.
Config.js
import { createContext } from 'react'
export default createContext({})
_App.js
import Config from '../Config'
export default function App({ Component, pageProps }) {
return (
<Config.Provider value={{ user: { name: 'John' }}}>
<Component {...pageProps} />
</Config.Provider>
)
}
Avatar.js
import { useContext } from 'react'
import Config from '../Config'
function Avatar() {
const { user } = useContext(Config)
return (
<span>
{user.name}
</span>
)
}
export default Avatar
No matter how you mount and dismount, you won't end up with re-render, as long as the _app doesn't.
Writable
The above example is only dealing with readable. If it's writable, you can try to pass a state into context. setUser will take care the set in consumer.
<Provider value={useState({})} />
const [user, setUser] = useContext(Config)
setUser is "cached" and won't be updated. So we can use this function to reset the user anytime in child consumer.
There're other ways, ex. React Recoil. But more or less you are dealing with a state management system to send a copy (either value or function) to somewhere else without touching other nodes. I'll leave this as an answer, since even we solved Layout issue, this problem won't disappear. And if we solve this problem, we don't need to deal with Layout at all.

Error in App class when using React-Redux "connect "

I am receiving the following error when trying to use the connect() function from react-redux:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of `App`.
This is App:
import { Provider } from 'react-redux';
import configureStore from './store';
const App = class extends React.PureComponent {
render() {
const { title } = this.context;
return (
<div className="center-screen">
{title}
<Provider store={configureStore()}>
<Chat />
</Provider>
</div>
);
}
};
This is the relevent code end of chat:
import { connect } from 'react-redux';
...
const mapStateToProps = state => ({
...state
});
const mapDispatchToProps = dispatch => ({
addMessage: () => dispatch(addMessage)
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Chat);
When using : "export default Chat" instead of connect, it's working fine..
Try this:
const ConnectedChat = connect(
mapStateToProps,
mapDispatchToProps
)(Chat);
export default ConnectedChat;
Or you may wish to rename the class definition to ConnectedChat and reverse the names so you can import it as just Chat.
Edit: Also make sure you're importing the Chat component in the App file, as well as the addMessage action creator if you're not.
Where are you defining Chat the component in your connect function?
My usual set up is (I'm using class but const ConnectedChart() would still be this same set up)
class ConnectedChart extends Component {
//// All code here render and return etc
}
const Chart = connect (mapStateToProps)(ConnectedChat);
export default Chart;
So that way you are essentially assigning a component to Chart with the connect statement and then you export default. I think exporting the connect statement directly might by throwing an error but if that doesn't work post the full chart component and I'll see if there is something else going on
EDIT: based on the full code
try this in your App.js:
import React from "react";
import ReactDOM from 'react-dom';
import "./App.css";
import ConnectedChat from "./Chat";
import { Provider } from "react-redux";
import configureStore from "./store";
ReactDOM.render(
<Provider store={configureStore()}>
<ConnectedChat />
</Provider>
);
and then put your div center screen in ConnectedChat
(if you are going to be adding more components later and want that div wrapping all of them, create a main app component like landing or something and call that between your provider instead of chat, and then in that landing component render the div and your ChatComponent)
Also if you don't have an index.js change the 'main' in your package.json to this App.js component

Is Fetching Data in App.js file allowed in React App?

I have built authorization into my React App using passport.js, and I would like to, in my App.js file, fetch my authorization routes to see if a user is logged into the app, or if nobody is logged in.
To help with the question, I have shared a condensed version of my React App's App.js file, and Index.js file.
// App.js File
// Import React Libraries, Routes, Container Pages
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router-dom';
import { userActions } from './actions/auth/auth-actions.js';
import GameLanding from './containers/StatsPages/Game/GameLanding';
import AppFooter from './components/AppFooter';
// And Create The App
class App extends Component {
constructor(props, context) {
super(props, context);
}
componentDidMount() {
this.props.dispatch(userActions.authorize());
}
render() {
return (
<div>
<Route exact path='/stats/games' render={() => <GameLanding userInfo={this.props.userInfo} />} />
<AppFooter />
</div>
);
}
}
// export default App;
function mapStateToProps(reduxState) {
return {
userInfo: reduxState.authorizedReducer.userInfo,
authorized: reduxState.authorizedReducer.authorized,
loading: reduxState.authorizedReducer.loading
};
}
export default connect(mapStateToProps)(App);
... my entire App.js file has ~15 Routes components, and (part of) my goal with my App.js file is to fetch the authorized and userInfo props, and pass these to the components in the various routes. I showed an example where I pass the userInfo prop to the GameLanding component.
Here is how I have set up my Index.js file.
// Index.js
// Import Libraries
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
// Import CSS and the App
import App from './App';
import 'react-table/react-table.css';
import './index.css';
import './App.css';
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById('root'));
My current problem is as such: For some reason, fetching the userInfo and authorized props is breaking my app. I am unfortunately getting no error messages... rather, all of the react-router-dom Links in my app are simply not working... clicking them changes the url, but the pages of my app no longer change...
My questions are then, (a) am i allowed to fetch authorization data in App.js in the manner I am doing so (using connect, with mapStateToProps, etc.), or am I doing this all wrong?
Whether or not somebody is logged into my app is an app-wide thing, not a page-specific thing, and I figured for this reason (also to prevent having to fetch auth props in many many container pages) that App.js is the best place to grab these props.
Any thoughts on why my app is breaking, or how else my App.js file should look (I am ~99% sure my index.js is fine), would be greatly appreciated! Thanks!
Edit: For reference, doing the following: (i) importing userActions, (ii) calling userActions.authorize() in componentDidMount, (iii) including the mapStateToProps and connect on bottom of app, etc. works for loading the auth props in any of my container components. e.g. if i had this code in my GameLanding component, it doesnt break the react-router-dom Links app-wide in the same manner that it does when this code is in App.js. Hence the title of the question. Thanks!
1) Reason for app breaking:
I am assuming userInfo and authorized props will be undefined, as component renders initially before componentDidMount runs and you have not handled undefined props. You could also pass default props for these props.
2) Better structure for authorization
I am assuming you need to authenticate each route for authorization.
i) Create routes file and enter all routes for your app.
ii) <Route exact path='/stats/games' component={GameLanding} onEnter={reqAuth}/>
Inside reqAuth function you should check if the user is authorized for that route or not.
iii) Inside App component call action for fetching data, store in store and use GameLanding as child component and pass props only when they are defined.
That is not whole code, but should give you gist.
Happy Coding!!!

React native redux export default connect

I do not want to separate components when I am using react-navigation with redux.
How can I make a "const=" rather than make a new file and "export default"
const IntroScreen2 =connect(mapStateToProps,mapDispatchToProps)(IntroScreen2a)
const IntroScreen2 =()=> connect(mapStateToProps,mapDispatchToProps)(IntroScreen2a)
export default connect ...
which one is right?
https://codeshare.io/G79NRk
Do it something like this, define the component in the same file as where you use a default export of connect, passing in the component defined in the file.
These statements should help clear up your misunderstanding(s).
With react navigation, you have screens (components), and you have navigators. Navigators are created with screens (components).
You use react-redux's connect function to connect components to the redux store. You simply wrap a component in a call to connect, and export the return value of that, rather than the component itself.
When you create a navigator, you will need to import the components for your screens.
See the follow three pages, we make a component, export the component connected to the redux store, via react-redux's connect function.
Then we make a router, which exports a single stack navigator from react navigation, which defines a single screen, the component defined (mentioned above).
Then I have given an example of how you'd render that router, for example, inside your App.js.
some-component.js
import React, {Component} from "react";
import {connect} from "react-redux"
// Define the component
class SomeComponent extends Component {
render() {
return null;
}
}
// Map dispatch to props
const mapDispatchToProps = (dispatch) => {
return {};
}
// Map state to props
const mapStateToProps = (state) => {
return {};
};
// Export the component, passed into the connect function from react-redux.
export default connect (mapStateToProps, {}) (SomeComponent);
Then just import this file when defining your navigator with react navigation.
For example
router.js
import SomeComponent from "./some-component.js";
import {createStackNavigator} from "react-navigation";
export default createStackNavigator({
PageOne: {
screen: SomeComponent
}
});
In your App.js (root level)
import React, {Component} from "react";
import Router from "./router.js";
export default class App extends Component {
render () {
return <Router/>;
}
}
Something like that should get you sorted!

Refresh contentComponent in react-navigation

I am using React-Navigation where I am using functionality of custom drawer by using contentComponent of React-Navigation.
const DrawerNavigation = DrawerNavigator({
DrawerStack: { screen: DrawerStack }
}, {
contentComponent: DrawerComponent,
drawerWidth: 300
})
Here DrawerComponent is my custom navigation drawer where I have used custom navigation items like username, profile picture, email address and other menus.
Now whenever user updates their profile I want to refresh my DrawerComponent, I am not able to find any way to do it. Can anybody suggest me a good way to implement this?
Couple of options here, and all are tight to how you want to achieve your state management.
First, one solution would be to have the your user state in the component creating the DrawerNavigator, and pass it down to your custom drawer component. This presents the disadvantage of having to recreate your navigator on state change and create a blink. I do not advice to use this solution but it's worth mentioning as a possibility.
You could also use a React Context, have your user state in a top level component, create a provider passing it the user as the value and make your drawer a consumer of this context. This way, every time the user changes your drawer component would re-render.
What I use personally is Redux to connect my Drawer directly to my global state. It involves a bit of setup but it's worth it in the end. A root component could look like this:
import React from 'react'
import { Provider } from 'react-redux'
export default () => (
<Provider store={store}>
<App />
</Provider>
)
Where store is the result of:
import { createStore, combineReducers } from 'redux'
import reducers from './reducers'
const store = createStore(combineReducers(reducers))
Your reducers are going to be the state of your app, and one would be dedicated to your user data.
Then your Drawer component could be:
import React, { Component } from 'react'
import { View, Text } from 'react-native'
import { connect } from 'react-redux'
#connect(({ user }) => ({ user }))
class Drawer extends Component {
render () {
const { user } = this.props
return (
<View>
<Text>My name is {user.name}</Text>
</View>
)
}
}
export default Drawer
Now, every time you change your user reducer, this Drawer component will re-render.
There is a few things your should know about Redux, so you should probably read up a bit the Getting Started docs.
I know it is a old question now but you can do this by importing the code like
import DrawerView from '../Drawer/Drawer'
contentComponent: DrawerView
then in the DrawerView file
class DrawerView extends Component {
render(){
return(
//Do your stuff here
)
}
}
export default DrawerView;
for more info please visit this link and thank to Kakul Gupta for this https://codeburst.io/custom-drawer-using-react-navigation-80abbab489f7
The easiest way to change menus without using redux is, using createSwitchNavigator.
https://reactnavigation.org/docs/en/auth-flow.html

Categories