Loading font native-base error - reactjs

I'm getting the error: You started loading 'Roboto_medium', but used it before it finished loading when using native base.
I've followed the instructions in the official page.
To create react native app I'm using create-react-native-app.
App.js
export default class App extends React.Component {
async componentWillMount() {
await Expo.Font.loadAsync({
'Roboto': require('native-base/Fonts/Roboto.ttf'),
'Roboto_medium': require('native-base/Fonts/Roboto_medium.ttf'),
'Ionicons': require('#expo/vector-icons/fonts/Ionicons.ttf'),
});
}
render() {
return (
<Container>
<StatusBar hidden={true} />
<Button>
<Text>
Button
</Text>
</Button>
<ListaItens />
</Container>
);
}
}

you need to wait till the fonts get loaded. You can do something like this
import React from "react";
import { StatusBar } from "react-native";
import { Container, Button, text, ListItem, Text } from "native-base";
import Expo from "expo";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true };
}
async componentWillMount() {
await Expo.Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
Ionicons: require("#expo/vector-icons/fonts/Ionicons.ttf"),
});
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <Expo.AppLoading />;
}
return (
<Container>
<StatusBar hidden={true} />
<Button>
<Text>Button</Text>
</Button>
<ListItem />
</Container>
);
}
}

This new code for expo SDK 35 which was modified from #akhil xavier 's answer
First install expo-font
expo install 'expo-font'
here is the App.js
import React from "react";
import * as Font from "expo-font";
import { ActivityIndicator } from "react-native";
import { Root } from "native-base";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { loading: true };
}
async componentWillMount() {
await Font.loadAsync({
Roboto: require("native-base/Fonts/Roboto.ttf"),
Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"),
Ionicons: require("#expo/vector-icons/build/vendor/react-native-vector-icons/Fonts/Ionicons.ttf"),
});
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <ActivityIndicator />;
}
return (
<Root>
<RootPage /> // starter component (i.e. nav)
</Root>
);
}
}

The solution that works for me is below. The error I had was that I imported Font as (Fonts) but while calling it failed to notice the 's'. Fixed it by making sure the import name is similar to what you call the loadAsync to. You need to have 'expo-font' installed in your project for it to work
import React from "react";
import * as Font from "expo-font";
import { AppLoading } from "expo";
import MealsNavigator from "./navigation/MealsNavigator";
const fetchFonts = () => {
return Font.loadAsync({
"open-sans": require("./assets/fonts/OpenSans-Regular.ttf"),
"open-sans-bold": require("./assets/fonts/OpenSans-Bold.ttf")
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false);
if (!fontLoaded) {
return (
<AppLoading
startAsync={fetchFonts}
onFinish={() => setFontLoaded(true)}
onError={err => console.log(err)}
/>
);
}
return <MealsNavigator />;
}

If anyone is having this issue with the 'MaterialIcons' font family, I had a similar problem and found following this solution worked:
https://javascriptrambling.blogspot.com.au/2018/03/expo-icon-fonts-with-react-native-and.html
You basically need to:
Install the fonts (using npm install)
Do a Font.loadAsync on the fonts in your componentWillMount() function.
Remember to mark the componentWillMount() function as async
The conditionally display either as 'loading' or with the view depending on the state of a 'loaded' flag.
For example:
import React from 'react';
import { View } from 'react-native';
import { Avatar } from 'react-native-elements';
import { AppLoading, Font } from 'expo';
import FontAwesome
from './node_modules/#expo/vector-icons/fonts/FontAwesome.ttf';
import MaterialIcons
from './node_modules/#expo/vector-icons/fonts/MaterialIcons.ttf';
export default class App extends React.Component {
state = {
fontLoaded: false
};
async componentWillMount() {
try {
await Font.loadAsync({
FontAwesome,
MaterialIcons
});
this.setState({ fontLoaded: true });
} catch (error) {
console.log('error loading icon fonts', error);
}
}
render() {
if (!this.state.fontLoaded) {
return <AppLoading />;
}
return (
<View>
<Text>My App</Text>
<Avatar
small
rounded
icon={{ name: 'add' }}
/>
</View>
);
}
}

This is the way which I use to load Fonts in my project. I feel this more abstracted as compared to all the other answers.
What you need to do is create a custom hook to Load fonts
You can do it something like this
Create a folder called hooks where your App.js is located. Then inside hooks folder create a file called useFonts.js
Inside useFonts.js write like this
import * as Font from 'expo-font';
export default useFonts = async () =>
await Font.loadAsync({
Roboto: require('native-base/Fonts/Roboto.ttf'),
Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
Ionicons: require('#expo/vector-icons/fonts/Ionicons.ttf'),
});
Now in your App.js write like this
import * as Font from 'expo-font';
import AppLoading from 'expo-app-loading';
import React, { useState } from 'react';
import useFonts from './hooks/useFonts';
export default function App() {
const [IsReady, SetIsReady] = useState(false);
// This function will start the fontLoading
const LoadFonts = async () => {
await useFonts();
};
// This is a check to ensure fonts get loaded
if (!IsReady) {
return (
<AppLoading
startAsync={LoadFonts}
onFinish={() => SetIsReady(true)}
onError={() => {}}
/>
);
}
// If fonts are successfully loaded then show the rest of your App
return (
<Container>
<StatusBar hidden={true} />
<Button>
<Text>Button</Text>
</Button>
<ListaItens />
</Container>
);
}

One reason why it has to load font is because you are using the Native Base Text component. If you import the React Native Text component instead you won't even have to load fonts and you won't see that error.

Related

Show preview_video image/thumbnail as preview before playing video in React

I have video that I'd like to present as preview_video image/thumbnail before a user clicks on them for the full video. Is there a RN component to do this?
I have this code:
import { Video } from 'expo-av';
import VideoPlayer from 'expo-video-player';
import * as React from "react";
import {View} from "react-native";
import Header from "../../components/header/Header";
export default function VideoPlayerComponent(props) {
const { url } = props.route.params;
return (
<View>
<Header
title="Video"
goBack
/>
<VideoPlayer
style={{height:550}}
videoProps={{
shouldPlay: true,
resizeMode: Video.RESIZE_MODE_CONTAIN,
source: {
uri: url,
},
}}
/>
</View>
);
}
Seems you can try this component: https://docs.expo.dev/versions/latest/sdk/video-thumbnails/
const generateThumbnail = async () => {
try {
const { uri } = await VideoThumbnails.getThumbnailAsync(
'http://d23dyxeqlo5psv.cloudfront.net/big_buck_bunny.mp4',
{
time: 15000,
}
);
setImage(uri);
} catch (e) {
console.warn(e);
}
Then just include a condition when the video component is in its paused state, the thumbnail should be displayed with a higher zIndex.
Like describe here: react native video display thumbnail before video is played

fontFamily is not a system font and has not been loaded through Font.loadAsync But with Font.loadAsync

I'm learning react native by creating some projects.
I got stuck and unable to load custom fonts. I've followed this guide from expo docs. but I'm getting an error
fontFamily "raleway-bold" is not a system font and has not been loaded
through Font.loadAsync.
Then I have use this Font.loadAsync in my code but I still get the same error.
Any suggestions?
import React, { useState } from 'react';
import Home from './screens/Home';
import AppLoading from 'expo-app-loading';
import * as Font from 'expo-font';
const getFonts = () =>
Font.loadAsync({
'raleway-regular': require('./assets/fonts/Raleway-Regular.ttf'),
'raleway-bold': require('./assets/fonts/Raleway-Bold.ttf'),
});
export default function App() {
const [fontsLoaded, setFontsLoaded] = useState(false);
if (fontsLoaded) {
return <Home />;
} else {
return (
<AppLoading
startAsync={getFonts}
onFinish={() => setFontsLoaded(true)}
onError={console.warn}
/>
);
}
}
This is an expo client and AppLoading bug GitHub issue
You can try an alternative approach and not use AppLoading as below
useEffect(() => {
async function getFonts() {
await fetchFonts();
setFontLoaded(true);
}
getFonts();
}, []);
if (!fontLoaded) {
return (
<View>
<Text>Loading...</Text>
</View>
);
}

How to fix memory leak issue in react native?

Problem:
My App component is giving me an error like this.
D:\projects\myapp\node_modules\react-native\Libraries\Core\ExceptionsManager.js:173 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method.
in App (at renderApplication.js:45)
This is how my code looks like.
/* eslint-disable prettier/prettier */
import React, {Component} from 'react';
import {StatusBar} from 'react-native';
import {Provider} from 'react-redux';
import RootNavigator from '_navigations/RootNavigator';
import AsyncStorage from '#react-native-community/async-storage';
import store from '_store';
import Splashscreen from '_screens/splashscreen';
import axios from 'axios';
import refreshToken from '_store/actions/refreshToken';
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
};
}
componentDidMount() {
this.updateLoading();
refreshToken();
}
updateLoading = async () => {
const data = await this.performTimeConsumingTask();
if (data !== null) {
this.setState({isLoading: false});
}
};
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve('result');
}, 3000),
);
};
render() {
if (this.state.isLoading) {
return <Splashscreen />;
}
return (
<>
<StatusBar barStyle="dark-content" />
<Provider store={store}>
<RootNavigator />
</Provider>
</>
);
}
}
export default App;
My splash screen is like this.
import React from 'react';
import {View, Image} from 'react-native';
import styles from './splashstyle';
import logo from '_assets/img/logo.png';
const Splashscreen = () => {
return (
<View style={styles.container}>
<Image source={logo} style={styles.logo} />
</View>
);
};
export default Splashscreen;
As it is saying the issue is inside rendered method but I do not where I have done wrong. I tried a lot to find out what where I have done wrong but it is giving me this issue over and over. Can someone help me to solve this issue?Thank you
if performTimeConsumingTask take so much time that the user moved to another screen(aka component) then the function updateLoading will continue its process which will try to setState in an unmounted component..
so you should cancel your work in componentWillUnmount to fix the issue
I would suggest some tweak like
class MyComponent{
constructor(){ ... this.isMounted = false; }
componentDidMount(){ ... this.isMounted = true; }
componentWillUnmount() { ... this.isMounted = false }
// then inside updateLoading
updateLoading = async () => {
const data = await this.performTimeConsumingTask();
if (this.isMounted && data !== null) {
// will protect set state against unmounted component
this.setState({isLoading: false});
}
};
}

React Navigation this.props.navigation.navigate doesn't navigate to the next screen

I have an Authentication screen like this :
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Profile from './Profile';
import LoginOrSignup from './LoginOrSignup';
class Auth extends Component {
render() {
if (this.props.isLoggedIn) {
return <Profile />;
} else {
return <LoginOrSignup />;
}
}
}
const mapStateToProps = (state, ownProps) => {
return {
isLoggedIn: state.auth.isLoggedIn
};
}
export default connect(mapStateToProps)(Auth);
On my LoginOrSignup screen i have a submit button contains a function to navigate like this:
this.props.navigation.navigate("ConfirmOTP");
But I always got this error :
undefined is not an object(evaluating 'this.props.navigation.navigate')
Then I tried to changed my Authentication screen like this
.....
<LoginOrSignup navigation={this.props.navigation}/>
.....
The error is gone, but it doesn't navigate to the next screen.
I'm still learning. Any helps would be really appreciated.
you need to register your sreen to a Route. This can be an example using createStackNavigator of react-navigation.
import ConfirmOtp from "./ConfirmOtp";
import { createStackNavigator } from "react-navigation";
class LoginOrSignup extends Component {
render() {
return (
<View>
<Button onClick={() => this.props.navigation.navigate("ConfirmOtp")}/>
</View>
)
}
}
export default createStackNavigator({
LoginOrSignup {
screen: LoginOrSignup
},
ConfirmOtp: {
screen: ConfirmOtp
}
});

React New Context API - Access Existing Context across Multiple Files

All the examples I've seen of the new Context API in React are in a single file, e.g. https://github.com/wesbos/React-Context.
When I try to get it working across multiple files, I'm clearly missing something.
I'm hoping to make a GlobalConfiguration component (the MyProvider below) create and manage the values in the context, ready for any child component (MyConsumer below) read from it.
App.js
render() {
return (
<MyProvider>
<MyConsumer />
</MyProvider>
);
}
provider.js
import React, { Component } from 'react';
const MyContext = React.createContext('test');
export default class MyProvider extends Component {
render() {
return (
<MyContext.Provider
value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider >
);
}
}
consumer.js
import React, { Component } from 'react';
const MyContext = React.createContext('test');
export default class MyConsumer extends Component {
render() {
return (
<MyContext.Consumer>
{(context) => (
<div>{context.state.somevalue}</div>
)}
</MyContext.Consumer>
);
}
}
Unfortunately that fails with this in the console:
consumer.js:12 Uncaught TypeError: Cannot read property 'somevalue' of undefined
Have I completely missed the point? Is there documentation or an example of how this works across multiple files?
I think the problem that you are running into is that you are creating two different contexts, and trying to use them as one. It is the Context created by React.createContext that links Provider and Consumer.
Make a single file (I'll call it configContext.js)
configContext.js
import React, { Component, createContext } from "react";
// Provider and Consumer are connected through their "parent" context
const { Provider, Consumer } = createContext();
// Provider will be exported wrapped in ConfigProvider component.
class ConfigProvider extends Component {
state = {
userLoggedIn: false, // Mock login
profile: { // Mock user data
username: "Morgan",
image: "https://morganfillman.space/200/200",
bio: "I'm Mogranโ€”so... yeah."
},
toggleLogin: () => {
const setTo = !this.state.userLoggedIn;
this.setState({ userLoggedIn: setTo });
}
};
render() {
return (
<Provider
value={{
userLoggedIn: this.state.userLoggedIn,
profile: this.state.profile,
toggleLogin: this.state.toggleLogin
}}
>
{this.props.children}
</Provider>
);
}
}
export { ConfigProvider };
// I make this default since it will probably be exported most often.
export default Consumer;
index.js
...
// We only import the ConfigProvider, not the Context, Provider, or Consumer.
import { ConfigProvider } from "./configContext";
import Header from "./Header";
import Profile from "./Profile";
import "./styles.css";
function App() {
return (
<div className="App">
<ConfigProvider>
<Header />
<main>
<Profile />
</main>
<footer>...</footer>
</ConfigProvider>
</div>
);
}
...
Header.js
import React from 'react'
import LoginBtn from './LoginBtn'
... // a couple of styles
const Header = props => {
return (
... // Opening tag, etc.
<LoginBtn /> // LoginBtn has access to Context data, see file.
... // etc.
export default Header
LoginBtn.js
import React from "react";
import Consumer from "./configContext";
const LoginBtn = props => {
return (
<Consumer>
{ctx => {
return (
<button className="login-btn" onClick={() => ctx.toggleLogin()}>
{ctx.userLoggedIn ? "Logout" : "Login"}
</button>
);
}}
</Consumer>
);
};
export default LoginBtn;
Profile.js
import React, { Fragment } from "react";
import Consumer from "./configContext"; // Always from that same file.
const UserProfile = props => {...}; // Dumb component
const Welcome = props => {...}; // Dumb component
const Profile = props => {
return (
<Consumer>
...
{ctx.userLoggedIn ? (
<UserProfile profile={ctx.profile} />
) : (<Welcome />)}
...
</Consumer>
...
Reading the source code of React-Context, they do
<MyContext.Provider value={{
state: this.state,
}}>
and
<MyContext.Consumer>
{(context) => <p>{context.state.age}</p>}
So if you do
<MyContext.Provider value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider>
You should get somevalue like that
<MyContext.Consumer>
{(context) => <div>{context.somevalue}</div>}
</MyContext.Consumer>
EDIT
What if you create a file called myContext.js with:
const MyContext = React.createContext('test');
export default MyContext;
and then import it like :
import MyContext form '<proper_path>/myContext';
As of right now, the two context you created in the files are not the same even thought the name is the same. You need to export the context that you created in one of the files, and use that through out.
so something like this, in your provider.js file:
import React, { Component } from 'react';
const MyContext = React.createContext();
export const MyContext;
export default class MyProvider extends Component {
render() {
return (
<MyContext.Provider
value={{ somevalue: 1 }}>
{this.props.children}
</MyContext.Provider >
);
}
}
then in your consumer.js file
import MyContext from 'provider.js';
import React, { Component } from 'react';
export default class MyConsumer extends Component {
render() {
return (
<MyContext.Consumer>
{(context) => (
<div>{context.somevalue}</div>
)}
</MyContext.Consumer>
);
}
}
I'm gonna throw my solution into the pot - it was inspired by #Striped and simply just renames the exports into something that makes sense in my head.
import React, { Component } from 'react'
import Blockchain from './cloudComputing/Blockchain'
const { Provider, Consumer: ContextConsumer } = React.createContext()
class ContextProvider extends Component {
constructor(props) {
super(props)
this.state = {
blockchain: new Blockchain(),
}
}
render() {
return (
<Provider value={this.state}>
{this.props.children}
</Provider>
)
}
}
module.exports = { ContextConsumer, ContextProvider }
Now it's easy to implement a ContextConsumer into any component
...
import { ContextConsumer } from '../Context'
...
export default class MyComponent extends PureComponent {
...
render() {
return (
<ContextConsumer>
{context => {
return (
<ScrollView style={blockStyle.scrollView}>
{map(context.blockchain.chain, block => (
<BlockCard data={block} />
))}
</ScrollView>
)
}}
</ContextConsumer>
)
}
I'm SO done with redux!
TLDR; Demo on CodeSandbox
My current method of solving the same problem is to use the Unstated library, which as a convenient wrapper around the React Context API. "Unstated" also provides dependency injection allow the creating of discrete instances of a container; which is handy for code reuse and testing.
How to Wrap a React/Unstated-Context as a Service
The following skeleton API Service holds state properties such as loggedIn, as well as two service methods: login() and logout(). These props and methods are now available throughout the app with a single import in each file that needs the context.
For example:
Api.js
import React from "react";
// Import helpers from Unstated
import { Provider, Subscribe, Container } from "unstated";
// APIContainer holds shared/global state and methods
class APIContainer extends Container {
constructor() {
super();
// Shared props
this.state = {
loggedIn: false
};
}
// Shared login method
async login() {
console.log("Logging in");
this.setState({ loggedIn: true });
}
// Shared logout method
async logout() {
console.log("Logging out");
this.setState({ loggedIn: false });
}
}
// Instantiate the API Container
const instance = new APIContainer();
// Wrap the Provider
const ApiProvider = props => {
return <Provider inject={[instance]}>{props.children}</Provider>;
};
// Wrap the Subscriber
const ApiSubscribe = props => {
return <Subscribe to={[instance]}>{props.children}</Subscribe>;
};
// Export wrapped Provider and Subscriber
export default {
Provider: ApiProvider,
Subscribe: ApiSubscribe
}
App.js
Now the Api.js module can be used as global provide in App.js:
import React from "React";
import { render } from "react-dom";
import Routes from "./Routes";
import Api from "./Api";
class App extends React.Component {
render() {
return (
<div>
<Api.Provider>
<Routes />
</Api.Provider>
</div>
);
}
}
render(<App />, document.getElementById("root"));
Pages/Home.js:
Finally, Api.js can subscribe to the state of the API from deep within the React tree.
import React from "react";
import Api from "../Api";
const Home = () => {
return (
<Api.Subscribe>
{api => (
<div>
<h1>๐Ÿ  Home</h1>
<pre>
api.state.loggedIn = {api.state.loggedIn ? "๐Ÿ‘ true" : "๐Ÿ‘Ž false"}
</pre>
<button onClick={() => api.login()}>Login</button>
<button onClick={() => api.logout()}>Logout</button>
</div>
)}
</Api.Subscribe>
);
};
export default Home;
Try the CodeSandbox demo here: https://codesandbox.io/s/wqpr1o6w15
Hope that helps!
PS: Someone bash me on the head quick if I'm doing this the wrong way. I'd love to learn different/better approaches. - Thanks!

Resources