I am trying to display an image in my app, using the documentation provided by React-Native. the code is here.
The only file that is concerned is the app.js file which has the code. the image is in the same folder as the app.js file. Can someone help me figure out why this might not be working?
the line of code in question is:
<Image source={require('./practicialogo.PNG')} />
I getting two errors:
1. Unable to resolve path to file (which i dont understand as the image is in the same folder as the file 2. ES Lint is giving me an unexpected require() (global require) error on that line where i call for the image.
Here is the entire file:
import React from 'react';
import { StyleSheet, Text, View, Image } from 'react-native';
import { StackNavigator } from 'react-navigation';
import { Button } from 'react-native-elements';
//import { Button } from './src/components/Button';
//import { CardSection } from './src/components/CardSection';
class HomeScreen extends React.Component {
static navigationOptions = {
title: 'WELCOME!'
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
<Image source={require('./practicialogo.PNG')} />
<Text>Hello, Chat App!</Text>
<Button
raised
backgroundColor="#3399ff"
borderRadius="20"
onPress={() => navigate('Chat', { user: 'Lucy' })}
title="CHAT WITH LUCY"
/>
</View>
);
}
}
class ChatScreen extends React.Component {
static navigationOptions = ({ navigation }) => ({
title: `Chat with ${navigation.state.params.user}`,
});
render() {
const { params } = this.props.navigation.state;
return (
<View>
<Text>Chat with {params.user}</Text>
</View>
);
}
}
const SimpleApp = StackNavigator({
Home: { screen: HomeScreen },
Chat: { screen: ChatScreen }
});
export default class App extends React.Component {
render() {
return <SimpleApp />;
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
}
});
My solution to the ESLint Error is to require images at the top of the file and then reference them in JSX like this:
const myImage = require('myImage.png');
<Image
source={myImage}
/>
I wouldn't concern yourself too much with the eslint error. Eslint isn't God, and some things you must do in RN are going to make it angry. As for the unresolved path, I know you already tried .png lowercase extension. Can you rename the image to a .png lowercase extension and then try that again? Your code should work, so that's all I can think of. Maybe throw it in an 'image' folder and require it from that path. I know it's essentially the same as what you already have, but sometimes just getting an image to show helps, and then work backwards from there.
You want your code to look like this:
source={require('./practicialogo.png')}
source={require('./image/practicialogo.png')} // or with it in an image folder
Extra curly braces and parenthesis might make the image unaccessible.
I hope that helps. Your code is correct, so that's all I can think of as to why it wouldn't find that image.
This is one of the rule from eslint to use require only on top as global reference
From eslint docs
In Node.js, module dependencies are included using the require()
function, such as:
var fs = require("fs");
While require() may be called anywhere in code, some style guides
prescribe that it should be called only in the top level of a module
to make it easier to identify dependencies. For instance, it’s
arguably harder to identify dependencies when they are deeply nested
inside of functions and other statements:
However, you can disable this rule for just that dependency using the // eslint-disable-line global-require comment. For eg if you want to disable it in jsx:
<View>
{/* eslint-disable-line global-require */}
<Image source={require('./practicialogo.PNG')} />
<Text>Hello, Chat App!</Text>
I tried requiring an image with uppercase extension and it's not working and throwing unable to resolve.
You may try to rename your practicialogo.PNG to practicialogo.png, then require('./practicialogo.png')
Related
Is there any existing workaround to show a splashscreen on web? It is not yet supported, and I'd like to avoid seeing a white screen while loading the website.
Ref.: https://docs.expo.io/versions/v41.0.0/sdk/splash-screen/
Known issue on github: https://github.com/expo/expo/issues/10839
I tested (and use) it with SDK 47 and adapted the example on https://docs.expo.dev/versions/latest/sdk/splash-screen/#usage like this (I simplified some components and functions here for better readability, so this example never "run" like this in reality):
import React, { useEffect, useState } from 'react';
import { Text, View, Platform } from 'react-native';
import Entypo from '#expo/vector-icons/Entypo';
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import { runAllTheInitStuff } from './init';
import SomeProvider from './SomeProvider';
import AnotherProvider from './AnotherProvider';
import WebSplashScreen from './WebSplashScreen';
// Keep the splash screen visible while we fetch resources
SplashScreen.preventAutoHideAsync();
export default function App() {
const [appIsReady, setAppIsReady] = useState(false);
useEffect(() => {
async function prepare() {
await runAllTheInitStuff();
// Tell the application to render
setAppIsReady(true);
// hide splash screen
await SplashScreen.hideAsync();
}
prepare();
}, []);
// check if app is ready
if(!appIsReady) {
// check if we are in web
if(Platform.OS === 'web') {
return <WebSplashScreen />;
} else {
return null;
}
}
return (
<SomeProvider>
<AnotherProvider>
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>SplashScreen Demo! 👋</Text>
<Entypo name="rocket" size={30} />
</View>
</AnotherProvider>
</SomeProvider>
);
}
I do not use a <View> component as first entry point, but a lot of provider, so it would be quite challenging to use onLayout prop in my case. That's the reason why hiding the splash screen is done directly in the useEffect hook...
The WebSplashScreen component can be anything (i.e. the splash screen used in mobile app as image or what ever), I use a simple activity indicator from a material ui library...
I need to create a custom font that applies to every Text component in the whole application.
Is there is a way to set a font globally in React Native?
One way is to create a wrapper for RN Text say MyTextCustomFont:
const MyTextCustomFont = (props) => {
return (
<Text style={{fontFamily:'myFont'}} {...props} >{props.children}</Text>
)
}
import this MyTextCustomFont and use anywhere.
Another way is to define a style object and use it wherever you want.
To do this we have to implement a method in which we will override Text component creation in React Native. In this we will set default font family or size or any attribute we want to set by default.
// typography.js
import React from 'react'
import { Text, Platform, StyleSheet } from 'react-native'
export const typography = () => {
const oldTextRender = Text.render
Text.render = function(...args) {
const origin = oldTextRender.call(this, ...args)
return React.cloneElement(origin, {
style: [styles.defaultText, origin.props.style],
})
}
}
const styles = StyleSheet.create({
defaultText: {
fontFamily: 'NunitoSans-Regular',//Default font family
}
});
Then in index.js you have to do this:
import { typography } from './src/utils/typography'
typography()
Detailed answer here:
https://ospfolio.com/two-way-to-change-default-font-family-in-react-native/
I think your problem is add Custom Fonts in react native.
1. Add Your Custom Fonts to Assets
Add all the font files you want to an “assets/fonts” folder in the root of your react native project:
2. Edit Package.json
Adding rnpm to package.json providing the path to the font files:
"rnpm": {
"assets": [
"./assets/fonts/"
]
},
3. Link assest files
run this command in your react native project root folder
react-native link
This should add the font references in your Info.plist file for iOS and on Android copy the font files to android/app/src/main/assets/fonts.
4. Add in stylesheet
Add a fontFamily property with your font name:
const styles = StyleSheet.create({
title: {
fontSize: 16,
fontFamily: 'PlayfairDisplay-Bold',
color: '#fff',
paddingRight: 20,
},
});
So, I've made a component doing this quite easely some times ago. This is working with Expo, I don't know for vanilla react-native.
at the start of your app:
import { Font, Asset } from 'expo'
async initFont() {
try {
await Font.loadAsync({
'Bariol': require('src/assets/font/Bariol_Regular.otf'),
'Bariol Bold': require('src/assets/font/Bariol_Bold.otf'),
})
this.setState({ fontReady: true })
} catch (e) {
console.log(e)
}
}
Then, you have to create a component file like text.js containing this code:
export default function (props) {
let font = { fontFamily: 'Bariol' }
if (props.bold) {
font = { fontFamily: 'Bariol Bold' }
}
const { bold, style, children, ...newProps } = props
return (
<Text {...newProps} style={[Style.text, props.style, font]}>
{props.children}
</Text>
)
}
Finally, in any of you other component / page just import MyText:
import Text from 'path/to/text.js'
use it like a normal Text component:
<Text bold>Hello World!</Text>
Even if this solution looks a bit more complicated than the others, it is easier to use once the setup is ok, since you just have to import Text.
You can override Text behaviour by adding this in any of your component using Text:
Edit: Add this code in your App.js or main file
let oldRender = Text.render;
Text.render = function (...args) {
let origin = oldRender.call(this, ...args);
return React.cloneElement(origin, {
style: [{color: 'red', fontFamily: 'Arial'}, origin.props.style]
});
}
For react Native Version 0.56 or below, Add this code in your App.js or main file
let oldRender = Text.prototype.render;
Text.prototype.render = function (...args) {
let origin = oldRender.call(this, ...args);
return React.cloneElement(origin, {
style: [{color: 'red', fontFamily: 'Arial'}, origin.props.style]
});
};
Reference
Or create your own component, such as MyAppText.
MyAppText would be a simple component that renders a Text component using your universal style and can pass through other props, etc.
I use a wrapper with default props like this :
const CustomText = ({ fontFam = "regular", ...props }) => {
const typo = {
light: "Montserrat_300Light",
regular: "Montserrat_400Regular",
bold: "Montserrat_600SemiBold",
};
return (
<Text {...props} style={[{ fontFamily: typo[fontFam], ...props.style }]}>
{props.children}
</Text>
);
};
export default CustomText;
By default, if "fontFam" is not indicated it will be regular font.
An example with bold typo :
<CustomText fontFam="bold" style={{ marginTop: 30, color: "grey" }}>
Some Text
</CustomText>
You can replace all your <Text/> by <CustomText />.
If you don't have to create custom component, you could try react-native-global-font. It will be apply for your all Text and TextInput
yes
app.js
import styles from './styles';
{...}
<Text style={styles.text}>hello World </Text>
{...}
styles.js
import {StyleSheet} from 'react-native';
const styles = StyleSheet.create({
text: {
// define your font or size or whatever you want to style here
},
use style on every text and all changes will affect all text components
I have a TabNavigator, and in each tab is a StackNavigator. Inside the StackNavigator, I have screens. The screens in each Tab do not call each other directly; the TabNavigator handles the screen changes when a tab is pressed.
In the first tab, if the user clicks a button, some data is created. If the user then navigates to the second Tab, I would like to pass this data to the screen in the second Tab.
Here is a demo of the code:
import React from 'react';
import { Button, Text, View } from 'react-native';
import {
createBottomTabNavigator,
createStackNavigator,
} from 'react-navigation';
class HomeScreen extends React.Component {
doIt = () => {
this.props.navigation.setParams({results: ['one', 'two']}); // <--- set data when user clicks button.
}
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
{/* other code from before here */}
<Button
title="Set Results"
onPress={this.doIt}
/>
</View>
);
}
}
class SettingsScreen extends React.Component {
render() {
console.log(this.props.navigation); // <--- console out when user clicks on this tab
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings</Text>
</View>
);
}
}
const HomeStack = createStackNavigator({
Home: HomeScreen,
});
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
});
export default createBottomTabNavigator(
{
Home: HomeStack,
Settings: SettingsStack,
},
{
}
);
The this.props.navigation.state.params never gets the data results in the second Tab. There isn't even a key for it, so if I try to access this.props.navigation.state.params.results, it will be undefined.
This is confusing because I thought props.navigation is passed to all screens automatically.
How can I pass data from one screen to another through the TabNavigator, using just react-navigation? I have seen answers that say to use Redux, but I would not like to import another library if all I want is to keep some state across screens in different react navigators.
It may seem that this.props.navigation.state.params is only able to old one parameter? Possibly? Try this:
doIt = () => {
this.props.navigation.setParams({results: 'one'}); // <--- set data when user clicks button.
}
console.log(this.props.navigation.state.params.results);
Setting props did not work when passing data across different tabs. I even tried playing with AsyncStorage, trying to save and retrieve them in different tabs.
I ended up using Redux to save my states, and that has worked well so far.
I came across a similar problem. I had a multi page form that the client insisted on having each step be enclosed in a tab on a tab bar. I used the react navigation createMaterialTopTabNavigator to create the navigator and couldn't find an easy way to pass the form data between tabs.
What I end up doing was using react's Context API and wrapped the tab navigator in a root form container that provides the context value to the navigator and routes inside. Here is how I did it:
Root form container
// MultiScreenForm.js
imports...
import MultiScreenFormNavigator from './MultiScreenFormNavigator'
export const FormContext = React.createContext()
class MultiScreenForm extends Component {
constructor(props) {
super(props)
this.state = {
// formDataHere
formUpdaters: {
onToggleOptIn: this.handleToggleOptIn // example updater method
// other
}
}
}
handleToggleOptIn = () => {
// toggle opt in form data with this.setState
}
render() {
return (
<FormContext.Provider value={this.state}>
<MultiScreenFormNavigator />
</FormContext.Provider>
)
}
}
export default MultiScreenForm
Example form page
// ProfileForm.js
imports...
import { FormContext } from './MultiScreenForm'
class ProfileForm extends Component {
render() {
// FormContext.Consumer uses function as child pattern
return (
<FormContext.Consumer>
{ (context) => (
// our form can now use anything that we pass through the context
// earlier, we passed the root form's state including an updater
<button onPress={context.formUpdaters.onToggleOptIn} />
// ...
)
}
</FormContext.Consumer>
)
}
}
export default ProfileForm
Tab navigator
// MultiScreenFormNavigator.js
imports...
import ProfileForm from './ProfileForm'
import { createMaterialTopTabNavigator } from 'react-navigation'
const MultiScreenFormNavigator = createMaterialTopTabNavigator(
{
Profile: ProfileForm,
// AnotherForm: AnotherForm
},
// { navigator options here... }
)
export default MultiScreenFormNavigator
We then render the MultiScreenForm instead of the tab navigator directly.
This worked for me but I feel there should be an easier way to do this. I hope people who read this can share their approaches.
#tempomax
tried same with AsyncStorage but data came in with a delay.
Sometimes you don't need Redux if your app stays small.
So tried to find a way without Redux.
Here is what I came up with
I hope it's not too late to answer.
Solved it with NavigationEvents and setting params to Route.
The problem with tab is that you can´t pass params to screen because navigation.navigate will be triggered automatically if createMaterialTopTabNavigator is swiped or clicked on non-active TabBar Button.
This can be solved with NavigationEvent like follow.
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const MyScreen = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus',payload)}
onDidFocus={payload => console.log('did focus',payload)}
onWillBlur={payload =>
/*
if screen is about to change this will be triggred
In screen 'MyScreen2' you can get it with navigation.params
*/
this.props.navigation.navigate('MyScreen2', { name: 'Brent' })
}
onDidBlur={payload => console.log('did blur',payload)}
/>
{/*
Your view code
*/}
</View>
);
export default MyScreen;
Now you can get the data in MyScreen2
/* 2. Get the param, provide a fallback value if not available */
const { navigation } = this.props;
const itemId = navigation.getParam('name', 'DefaultName');
const otherParam = navigation.getParam('otherParam', 'some default value');
If you are using React Native Navigation Version 5.x with a DrawerNavigation, you can do this using
in screen 1:
<Button
onPress={() => {
this.props.navigation.navigate(<ScreenNameOfDrawerScreen>,
{screen:'<ScreenNameInTabDrawer>',params:{your_json_Data}});
}} />
in screen 2:
............
render() {
if(this.props.route.params!=undefined){
if(this.props.route.params.your_json_Data!=null){
// Use this.props.route.params.your_json_Data. It is your json data.
}
}
return (
..............
Dependencies version:
"dependencies": {
"react": "16.3.1",
"react-native": "~0.55.2",
"react-navigation": "^2.0.1",
}
I use react-navigation to navigate my screen, i create two screen and a drawer
Router.js:
import { createStackNavigator, createDrawerNavigator } from 'react-navigation';
import MainActivity from './components/MainActivity';
import ThisWeek from './components/ThisWeek';
import DrawerPanel from './components/DrawerPanel';
const Stack = createStackNavigator({
MainActivity: {
screen: MainActivity,
navigationOptions: {
title: 'Welcome',
headerStyle: {
backgroundColor: '#81A3A7',
elevation: null
}
}
},
ThisWeek: {
screen: ThisWeek
}
},
{
initialRouteName: 'MainActivity'
}
);
const Router = createDrawerNavigator({
FirstScreen: {
screen: Stack
}
},
{
contentComponent: DrawerPanel,
drawerWidth: 200
});
export default Router;
In my DrawerPanel.js i can click the two button navigate to the screen, but when i try to use this.props.navigation.closeDrawer(); that shows an error _this2.props.navigation.closeDrawer is not a function.
So i try to console.log(this.props);,i can see openDrawer and closeDrawer under navigation
Here is my DrawerPanel.js:
import React, { Component } from 'react';
import { ScrollView, FlatList, Text } from 'react-native';
import { View } from 'react-native-animatable';
import { List, Button } from 'react-native-elements';
class DrawerPanel extends Component {
render() {
console.log(this.props);
return (
<ScrollView style={{ backgroundColor: '#81A3A7' }}>
<Button
onPress={() => {
this.props.navigation.actions.closeDrawer();
this.props.navigation.navigate('MainActivity');
}}
backgroundColor={'#81A3A7'}
containerViewStyle={{ width: '100%', marginLeft: -61 }}
title='Main page'
/>
<Button
onPress={() => this.props.navigation.navigate('ThisWeek')}
backgroundColor={'#81A3A7'}
containerViewStyle={{ width: '100%', marginLeft: -46 }}
title='This weel'
/>
</ScrollView>
);
}
}
export default DrawerPanel;
I can't figure it out why i can use this.props.navigation.navigate(); to another screen not allow to use this.props.navigation.closeDrawer();
I try make a change to use this.props.navigation.actions.closeDrawer(); the error will show Cannot read property 'closeDrawer' of undefined.
What step i make it wrong ? Any help would be appreciated. Thanks in advance.
Try this
import { DrawerActions } from 'react-navigation';
this.props.navigation.dispatch(DrawerActions.closeDrawer());
this.props.navigation.dispatch(DrawerActions.openDrawer());
You are using both StackNavigator and DrawrNavigator so that the way to use them is different a little bit.
Keep in mind we have two version for react-navigation (v1 and v2) for now so that you should read the documentation carefully.
Please try using this:
Close drawer
this.props.navigation.navigate('DrawerClose'); // for version 1
this.props.navigation.openDrawer(); // for version 2
Open drawer:
this.props.navigation.navigate('DrawerOpen'); // for version 1
this.props.navigation.closeDrawer(); // for version 2
Be careful for reference any bugs fix for this libraries on the internet which you have to know which version is using in.
Note that the order of nesting - if the stack navigator is not inside of the drawer, it will not have the openDrawer function.
I guess it will work for you in this case.
Cheer!
In your DrawerPanel file. You did not use the constructor. Hence, your component's props did not know about the navigation props that the DrawerNavigator passed in. Try putting this before your render function in the component.
constructor(props){
super(props);
}
This way you will no longer need to use dispatch(), but instead use openDrawer() and closeDrawer().
I'm trying to create a menu button in the header area of one of my screens.
However, I can't figure out how to get the onPress navigation to work.
Obviously, the code below will produce an error because navigate is defined after it's first used.
But, when I try to move the const { navigate } declaration up before I set the navigationOptions property, I get an "unexpected token" error in my IDE (visual studio code).
Is there any way around this?
Thanks!
export default class HomeScreen extends React.Component
{
constructor(props) {
super(props);
}
static navigationOptions = {
title: 'Seekers',
headerRight:
<TouchableHighlight onPress={ () => navigate('Menu', {
menu: options
})}>
<Image source={require('./images/menu.png')} style={{width: 50, height: 50}} />
</TouchableHighlight>,
};
render() {
const { navigate } = this.props.navigation;
return (
<View>
... more view stuff...
Customizing the header changed in the last few updates of React Native and passing navigation options changed too. According to the doc Screen Navigation Options, Dynamic configuration is possible and the 'navigationOptions' now can be used as a function which takes navigation Options as parameters.
For example, I wanted a button to close and open my DrawerNavigator in the header. In my router.jsfile, I wrote:
export const Root = StackNavigator({
App: {
screen: AppDrawer,
navigationOptions: ({navigation}) => ({
headerLeft: <BurgerMenu nav = {navigation} />,
})
}
}, {
headerMode: 'screen',
});
In your case, I think you should declare your navigationOptions as a function:
static navigationOptions: ({navigation}) => ({
title: 'Seekers',
headerRight: <TouchableHighlight onPress={ () =>
navigation.navigate('Menu', { menu: options })}>
<Image source={require('./images/menu.png')} style={{width: 50, height: 50}} />
</TouchableHighlight>,
};
I didn't personnally try to set navigationOptions from the component itself. Instead I declare the options in my StackNavigator.
Let me know if it works !
EDIT
How does my navigation work ?
For the navigation side of my application I'm simply using react-navigation.
It would be pretty long to copy all the content of my files here so I created a Gist for you to see how I implemented that.
First, I create a router.js file, in which I define my screens and the type of navigation (here). Focus on the variable named Root, this is my main StackNavigator. This is the entry point of my app.
Second, I edited the index.android.js and index.ios.js so they both render the same component.
import { AppRegistry } from 'react-native';
import App from './src/index';
AppRegistry.registerComponent('obexto_expensemanager', () => App);
Third. What does my App component contains ? This component is the one I use to render my entry point defined in the router.js. What is important in this file is:
import { Root } from './config/router';
class App extends Component {
render() {
return (
<Root />
)
}
}
I use the StackNavigator defined in router.js as my main component for my app.
Why ? I like the way this navigation works because you separate clearly your stacks from your components. And all your navigation is set in a single file. So, in your project you'll have your scene folder with all your components and a router where you connect them.
In my example I wanted to add a button in my header so I could open my DrawerNavigator. The header can be accessed from the navigationOptions when you define your Navigator, that's why I defined the option headerLeft in my App StackNavigator, so it's visible in every screen.
I don't know if this is clear for you but don't hesitate to tell me if it's not and what is still obscur to you. I'll do my best to help !
Just a thought, how about declaring the navigationOptions directly on the StackNavigator (assuming that's what you are using). For example
`
const Nav = StackNavigator({yourRoutes}, {
{
navigationOptions: {...}
}
});
`