I have a React Native application and it has tabbed layout. I need to call some function when the main screen is loaded. I tried to use componentWillMount() function, but it didn't work because my screen was defined in function and not in class. How can I create on load function?
HomeScreen.js
import React, { useState, Component } from 'react';
import { View, Text } from 'react-native';
import { getText } from '..components/getText';
export default function HomeScreen() {
const [onLoadText, setText] = useState("");
const onScreenLoad = () => {
setText(getText());
}
const componentWillMount = () => {
// Not working
onScreenLoad();
}
return (
<View style={styles.container}>
<Text>{onLoadText}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
since you're using a stateless component, you can use the useEffect hook
useEffect(() => {
// write your code here, it's like componentWillMount
}, [])
You must add useEffect
import React, { useState, Component, useEffect } from 'react';
import { View, Text } from 'react-native';
import { getText } from '..components/getText';
export default function HomeScreen() {
const [onLoadText, setText] = useState("");
const onScreenLoad = () => {
setText(getText());
}
useEffect(() => {
// write your code here, it's like componentWillMount
onScreenLoad();
}, [])
return (
<View style={styles.container}>
<Text>{onLoadText}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
Related
I'm trying to load downloaded fonts along with google fonts using Font.loadAsync but it is giving me error:
Unable to resolve module ../../assets/fonts/sf-ui-text/SFUIText-Bold.ttf from /Users/bilal/Work/datumbrain/erp-app/src/utils/useFonts.js:
None of these files exist:
* SFUIText-Bold.ttf
* assets/fonts/sf-ui-text/SFUIText-Bold.ttf/index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.svg|.native.svg|.svg)
Here is my code in App.js:
import { useCallback, useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Provider } from 'react-redux';
import colors from './src/constants/colors';
import AppNavigator from './src/navigation/AppNavigator';
import { store } from './src/redux/store';
import * as SplashScreen from 'expo-splash-screen';
import * as Font from 'expo-font';
import {
FiraSans_300Light,
FiraSans_400Regular,
FiraSans_500Medium,
} from '#expo-google-fonts/fira-sans';
import { useFonts } from './src/utils/useFonts';
const App = () => {
const [appIsReady, setAppIsReady] = useState(false);
const prepare = async () => {
try {
const customFonts = await useFonts();
await SplashScreen.preventAutoHideAsync();
await Font.loadAsync({
'Fira Sans 300': FiraSans_300Light,
'Fira Sans 400': FiraSans_400Regular,
'Fira Sans 500': FiraSans_500Medium,
...customFonts,
});
} catch (e) {
console.warn(e);
} finally {
setAppIsReady(true);
console.log('Loaded fonts...');
}
};
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
useEffect(() => {
prepare();
}, []);
useEffect(() => {
onLayoutRootView();
}, [onLayoutRootView]);
if (!appIsReady) {
return null;
}
return (
<Provider store={store} style={styles.container}>
<AppNavigator />
</Provider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.light,
alignItems: 'center',
justifyContent: 'center',
},
});
export default App;
and useFonts.js:
import * as Font from 'expo-font';
export const useFonts = async () => {
await Font.loadAsync({
SF: require('../../assets/fonts/sf-ui-text/SFUIText-Bold.ttf'),
});
};
Please make sure the path here is correct
SF: require('../../assets/fonts/sf-ui-text/SFUIText-Bold.ttf')
For the Font.loadAsync, it looks perfect to me. For extra, you could use useFont hook to make your handling looks better. (It uses Font.loadAsync underneath)
Сheck the file path, this error occurs when the path is invalid.
Your useFonts function doesn't return anything, so you don't need spread in prepare(). If it is called fonts will be loaded anyway.
I'm building a screen in React Native using expo. I'm new to both React Native and the Rematch framework, and I want to render the first and last names of the basketball players from this endpoint upon load: https://www.balldontlie.io/api/v1/players
Here's my models.js:
import axios from "axios";
export const players = {
state: {
players: [],
},
reducers: {
SET_PLAYERS: (state, payload) => {
return {
...state,
players: payload,
};
},
},
effects: (dispatch) => ({
async getPlayers() {
let response = await axios.get(
"https://www.balldontlie.io/api/v1/players"
);
let { data } = await response.json();
console.log(data);
dispatch.players.SET_PLAYERS(data);
},
}),
};
store.js
import { init } from "#rematch/core";
import * as models from "./models";
const store = init({ models });
export default store;
And finally, my main screen:
import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { Provider } from "react-redux";
import store from "./state_management/store";
export default function App() {
return (
<View style={styles.container}>
<Players />
<StatusBar style="auto" />
</View>
);
}
const Players = () => {
return (
<Provider store={store}>
// PLAYER LIST HERE!!
</Provider>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
});
Most examples I have seen online and on here are to do with onPress actions like increments, decrements etc. This involves managing data from an API call so I'm not sure what to do.
In main screen as you are using functional component try using useDispatch and useSelector to dispatch and fetch the list and map it to your screen
import { useDispatch, useSelector } from "react-redux";
const dispatch = useDispatch();
const responseList = useSelector(state => {
return state.apiReducer.data
})
In useEffect dispatch action to models
As from the question what i understand is a need of fetch the stored list from models and render players name, find code for the same in this link
I'm starting to work with the context api in React, and I decided to make a simple theme context. For now, if I go to my React DevTools and change the boolean isLightTheme to false, it works, but when i try to attach this functionality to a button, i keep getting errors. Can saomeone help me with that?
theme context
import React, { useState, createContext } from 'react'
export const ThemeContext = createContext()
export const ThemeContextProvider = ({ children }) => {
const [state, setState] = useState({
isLightTheme: true,
light: { syntax: '#555', ui: '#ddd', bg: '#eee' },
dark: { syntax: '#ddd', ui: '#333', bg: '#555' }
})
const toggleTheme = () => {
setState({ isLightTheme: !state.isLightTheme})
}
return (
<ThemeContext.Provider value={{...state, toggleTheme}}>
{children}
</ThemeContext.Provider>
)
}
import React, { useContext } from 'react'
import { ThemeContext } from '../context/ThemeContext'
export const ThemeToggler = () => {
const themeContext = useContext(ThemeContext)
const { toggleTheme } = themeContext
return (
<button onClick={toggleTheme}>Change Theme</button>
)
}
The useState hooks API doesn't update a selective piece of state like setState does. It'll override the entire state. Here when you toggle the theme, the new state is only
{ isLightTheme: !state.isLightTheme} with no light and dark keys. You just need to handle this by only updating that piece of state. This should work :
import React, { useState, createContext } from 'react'
export const ThemeContext = createContext()
export const ThemeContextProvider = ({ children }) => {
const [state, setState] = useState({
isLightTheme: true,
light: { syntax: '#555', ui: '#ddd', bg: '#eee' },
dark: { syntax: '#ddd', ui: '#333', bg: '#555' }
})
const toggleTheme = () => {
//preserve the remaining state also
setState({...state,isLightTheme: !state.isLightTheme})
}
return (
<ThemeContext.Provider value={{...state, toggleTheme}}>
{children}
</ThemeContext.Provider>
)
}
Hope this helps !
It says I can use Expo.Apploading but I have no idea about that. If someone could help it would be great. This happens in either of device I am running. Below is code for App.js
import React, {useState}from 'react';
import {Text, View } from 'react-native';
import *as Font from 'expo-font';
import {AppLoading} from 'expo';
import {enableScreens} from 'react-native-screens';
import EstesGuideNavigator from './navigation/EstesGuideNavigator';
enableScreens(); // useScreens has been changed to enableScreens - this helps screens load faster in background
const fetchFonts = () => { //fetching costum fonts for my app using Async
Font.loadAsync({
'raleway-blackItalic' : require('./assets/fonts/Raleway-BlackItalic.ttf'),
'raleway-bold' : require('./assets/fonts/Raleway-Bold.ttf'),
'raleway-regular' : require('./assets/fonts/Raleway-Regular.ttf'),
'raleway-thin' : require('./assets/fonts/Raleway-Thin.ttf')
});
};
export default function App() {
const [fontLoaded, setFontLoaded] = useState(false); //initially it's false because app hasn't been loaded
if (!fontLoaded) {
return(
<AppLoading
startAsync = {fetchFonts}
onFinish = {() => setFontLoaded(true) }
/> //if assets(fonts here) is not loaded we display loading screen and load assets for app
);
}
return <EstesGuideNavigator/>;
}
Is there any way to resolve this?
I don't know what's wrong but this is how i handle my font loading, Works perfectly
import { AppLoading } from "expo";
import { Asset } from "expo-asset";
import * as Font from "expo-font";
import React, { useState } from "react";
import { Platform, StatusBar, StyleSheet, View } from "react-native";
import { Ionicons } from "#expo/vector-icons";
import AppNavigator from "./navigation/AppNavigator";
export default function App(props) {
const [isLoadingComplete, setLoadingComplete] = useState(false);
if (!isLoadingComplete && !props.skipLoadingScreen) {
return (
<AppLoading
startAsync={loadResourcesAsync}
onError={handleLoadingError}
onFinish={() => handleFinishLoading(setLoadingComplete)}
/>
);
} else {
return (
<View style={styles.container}>
{Platform.OS === "ios" && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
);
}
}
async function loadResourcesAsync() {
await Promise.all([
Asset.loadAsync([
require("./assets/images/tick.png"),
require("./assets/images/error.png")
]),
Font.loadAsync({
// This is the font that we are using for our tab bar
...Ionicons.font,
// We include SpaceMono because we use it in HomeScreen.js. Feel free to
// remove this if you are not using it in your app
"space-mono": require("./assets/fonts/SpaceMono-Regular.ttf"),
"Gilroy-Bold": require("./assets/fonts/Gilroy-Bold.otf"),
"Gilroy-Medium": require("./assets/fonts/Gilroy-Medium.otf"),
"Gilroy-Heavy": require("./assets/fonts/Gilroy-Heavy.otf")
})
]);
}
function handleLoadingError(error) {
// In this case, you might want to report the error to your error reporting
// service, for example Sentry
console.warn(error);
}
function handleFinishLoading(setLoadingComplete) {
setLoadingComplete(true);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff"
}
});
I don't understand how I'm getting this error (pic below). In my LoginForm.js file, the onEmailChange(text) is giving me an unresolved function or method call to onEmailChange() error when I hover over it in my WebStorm IDE. In my index.js file, no error is being thrown anywhere.
I've looked around SO for this issue but it doesn't fully pertain to my problem.
I've tried File > Invalidate Caches/Restart but that didn't work.
Here's App.js:
import React, { Component } from 'react';
import {StyleSheet} from 'react-native';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import firebase from 'firebase';
import reducers from './reducers';
import LoginForm from './components/common/LoginForm';
class App extends Component {
render() {
return(
<Provider style={styles.c} store={createStore(reducers)}>
<LoginForm/>
</Provider>
);
}
}
const styles = StyleSheet.create({
c: {
flex: 1
}
});
export default App;
Here's LoginForm.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {emailChanged} from 'TorusTeensApp/src/actions';
import {Text, StyleSheet, KeyboardAvoidingView, TextInput, TouchableOpacity} from 'react-native';
class LoginForm extends Component {
render() {
onEmailChange(text)
{
this.props.emailChanged(text);
}
return(
<KeyboardAvoidingView style={styles.container}>
<TextInput
style={styles.userInput}
onsubmitediting={() => this.passwordInput.focus()}
returnKeyType={"next"}
placeholder={"Email"}
label={"Email"}
keyboardType={"email-address"}
autoCorrect={false}
onChangeText={this.onEmailChange.bind(this)}
value={this.props.email}
/>
<TextInput
style={styles.userInput}
ref={(userInput) => this.passwordInput = userInput}
returnKeyType={"go"}
placeholder={"Password"}
label={"Password"}
secureTextEntry
/>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.buttonText}>Create Account</Text>
</TouchableOpacity>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
container: {
padding: 20 // creates a gap from the bottom
},
userInput: {
marginBottom: 20,
backgroundColor: '#9b42f4',
height: 40
},
buttonContainer: {
backgroundColor: '#41bbf4',
paddingVertical: 10,
marginBottom: 20
},
buttonText: {
textAlign: 'center',
color: '#FFFFFF'
}
});
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
export default connect(mapStateToProps, null, {emailChanged}) (LoginForm);
Here's index.js:
import {EMAIL_CHANGED} from './types';
export const emailChanged = (text) => {
return {
type: 'EMAIL_CHANGED',
payload: text
};
};
export default emailChanged();
Your connect is miswired
connect(mapStateToProps, null, {emailChanged}) (LoginForm);
It should be something like:
connect(mapStateToProps,
(dispatch) => ({emailChanged: (text) => dispatch(emailChanged(text))})
)(LoginForm);
so that your action actually gets dispatched
and as spotted by emed in comment:
export default emailChanged;
without parentheses.
You defined your callback inside your render() method and not inside the class body. Do it like this:
class LoginForm extends Component {
onEmailChange(text) {
this.props.emailChanged(text);
}
render() {
return(...);
}
}
Also you shouldn't bind methods inside your render() method. Do it in the constructor of your Component:
class LoginForm extends Component {
constructor(props) {
super(props);
this.onEmailChange.bind(this);
}
onEmailChange(text) {
// do something
}
// other methods
}
Or if you use babel and ES6, you can define your callback with an arrow function, then it will be automatically bound:
class LoginForm extends Component {
onEmailChange = text => {
// do something
};
// other methods
}
See also the react docs about autobinding.
Also your call to connect seems incorrect. If you want to dispatch the action emailChanged it has to look like this:
const mapStateToProps = state => {
return {
email: state.auth.email
};
};
const mapDispatchToProps = dispatch => {
// this put a function emailChanged into your props that will dispatch the correct action
emailChanged: text => dispatch(emailChanged(text))
};
const LoginFormContainer = connect(mapStateToProps, mapDispatchToProps)(LoginForm);
export default LoginFormContainer;
The third argument to connect needs to be a function that knows how to merge the output of mapStateToProps, mapDispatchToProps, and ownProps all into one object that is then used as props for your connected component. I think you're trying to pass that action to the mapDispatchToProps argument, which is the second argument not the third. So, based on what I think you're doing, you probably wanna change your connect line to look like this.
export default connect(mapStateToProps, {emailChanged}) (LoginForm);
Then, export the function from your actions file not the output of calling that function.
export default emailChanged;
Notice I removed the parentheses so it's not being called.
Then make the callback function a method on your class and bind it in the constructor.
constuctor(props) {
super(props);
this.onEmailChange = this.onEmailChange.bind(this);
}
onEmailChange(text) {
this.props.emailChanged(text);
}
Then update onChangeText on that element.
onChangeText={this.onEmailChange}