converting useffect into class component - react-native

const displayNameRef = useRef('');
useEffect(() => {
(async () => {
const loginMethod = await AsyncStorage.getItem('login-method');
if (loginMethod === 'google') {
displayNameRef.current = await AsyncStorage.getItem('google-user-name');
setState((s) => s + 1);
} else {
displayNameRef.current = 'Randomly_generated';
}
})();
}, []);
this is my useffect, here i getitem(display name )from async storage but i want to convert all the hooks into class component
<Header
title={('hi', displayNameRef.current)}
setSelectedTab={this.setSelectedTab}
selectedTab={this.state.selectedTab}
navigation={this.props.navigation}
openDrawerPanel={this.openDrawerPanel}
/>
so here i want to display displayNameRef.current inside of header

If you want to turn your hooks into class component, try to process à below:
class SomeCalss extends React.Component {
constructor(props) {
super(props);
this.state = {
s: 0
}
this.displayNameRef = React.createRef();
}
async componentDidMount() {
const loginMethod = await AsyncStorage.getItem('login-method');
if (loginMethod === 'google') {
displayNameRef.current = await AsyncStorage.getItem('google-user-name');
this.setState({s: this.state.s + 1});
} else {
displayNameRef.current = 'Randomly_generated';
}
}
render() {
return <Header
title={('hi', this.displayNameRef.current)}
setSelectedTab={this.setSelectedTab}
selectedTab={this.state.selectedTab}
navigation={this.props.navigation}
openDrawerPanel={this.openDrawerPanel}
/>
}
}
But you have to be aware that displayNameRef.current = 'Randomly_generated'; doesn't trigger re-render

Related

consuming Context on a Class Component

I'm new to context so may be a very easy error. I'm getting this error when triing to update the context:
[Unhandled promise rejection: TypeError: setUserInfo is not a function. (In 'setUserInfo(newUser)', 'setUserInfo' is undefined)]
Here is what I've done:
AppContext.js
import React, { Component } from 'react'
const AppContext = React.createContext();
class UserProvider extends Component {
// Context state
state = {
userInfo: {},
userChats: {},
userSettings: {},
}
// Method to update state
setUserInfo = (user) => {
this.setState((prevState) => ({ user }))
}
// Method to update state
setUserChats = (userChats) => {
this.setState((prevState) => ({ userChats }))
}
// Method to update state
setUserSettings = (settings) => {
this.setState((prevState) => ({ settings }))
}
render() {
const { children } = this.props
const { userInfo } = this.state
const { setUserInfo } = this
const { userChats } = this.state
const { setUserChats } = this
const { userSettings } = this.state
const { setUserSettings } = this
return (
<UserContext.Provider
value={{
userInfo,
setUserInfo,
userChats,
setUserChats,
userSettings,
setUserSettings,
}}
>
{children}
</UserContext.Provider>
)
}
}
export default AppContext
export { UserProvider }
Wrapping the App Component:
const defaultProviderValue = {userInfo: {}, userChats: {}, userSettings: {}}
<AppContext.Provider value = {defaultProviderValue}>
<Container>
<AppNavigator />
</Container>
</AppContext.Provider>
and then finally trying to update it on a class component:
import React, { Component} from 'react';
import AppContext from '../Context/AppContext.js'
class startScreen extends Component {
static contextType = AppContext
constructor(props) {
super(props);
this.state = {
};
}
async componentDidMount() {
const { userInfo, setUserInfo } = this.context
console.log("CONTEXT!!!!! : " + JSON.stringify(userInfo));
const newUser = { name: 'Joe', loggedIn: true };
setUserInfo(newUser); // ERROR fired: [Unhandled promise rejection: TypeError: setUserInfo is not a function. (In 'setUserInfo(newUser)', 'setUserInfo' is undefined)]
console.log("NEW CONTEXT!!!!! : " + JSON.stringify(userInfo));
}
render() {
return(null);
}
}
export default startScreen;
So how can I solve the error? It seems that can not find the method to update the value, but it's defined.
You are trying to use AppContext but you haven't set anything on itt apart from defaultValues. I guess you would want to use UserProvider and use AppContext within it
import React, { Component } from 'react'
const AppContext = React.createContext();
class UserProvider extends Component {
// Context state
state = {
userInfo: {},
userChats: {},
userSettings: {},
}
// Method to update state
setUserInfo = (user) => {
this.setState((prevState) => ({ user }))
}
// Method to update state
setUserChats = (userChats) => {
this.setState((prevState) => ({ userChats }))
}
// Method to update state
setUserSettings = (settings) => {
this.setState((prevState) => ({ settings }))
}
render() {
const { children } = this.props
const { userInfo } = this.state
const { setUserInfo } = this
const { userChats } = this.state
const { setUserChats } = this
const { userSettings } = this.state
const { setUserSettings } = this
return (
<AppContext.Provider
value={{
userInfo,
setUserInfo,
userChats,
setUserChats,
userSettings,
setUserSettings,
}}
>
{children}
</AppContext.Provider>
)
}
}
export default AppContext;
export { UserProvider };
<UserProvider>
<Container>
<AppNavigator />
</Container>
</UserProvider>
Post this change, you will be able to consume the context correctly

How to add a condition for the user's connection?

I want to add to this code the condition: if the user is connected, he goes directly to BottomTabNavigator (which is the opening of the application) and otherwise he goes in the Authentication file which will allow him to either connect or register. How can I do this ?
Usually I used
import React from "react";
import { NavigationContainer } from "#react-navigation/native";
import BottomTabNavigator from "./Navigation/TabNavigator";
const App = () => {
return (
<NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>
);
}
export default App
Usually in a class component I used this, but I don't know how to do with the new syntax:
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isFirstConnection: true,
status: 0,
fontLoaded: false
};
}
async UNSAFE_componentWillMount() {
let lang = await retrieveAppLang();
let isConnected = await userSessionActive();
if (lang.length == 2) {
i18n.changeLanguage(lang);
}
if (isConnected === true && this.props && this.props.navigation) {
this.props.navigation.navigate("TabBar");
}
}
async componentDidMount() {
await Font.loadAsync({
FunctionLH: require("./assets/fonts/FunctionLH-Light.ttf")
});
const data = await this.performTimeConsumingTask();
if (data !== null) {
this.setState({
isFirstConnection: false,
status: 1,
fontLoaded: true,
});
}
}
performTimeConsumingTask = async () => {
return new Promise((resolve) =>
setTimeout(() => {
resolve("result");
}, 750)
);
};
render() {
if (this.state.status == 1) {
if (this.state.isFirstConnection && this.state.fontLoaded) {
return <SplashScreen />;
} else {
return <Navigation screenProps={'Authentication'} />;
}
}
return (
<ImageBackground
source={require("./assets/images/background.jpg")}
style={{ flex: 1 }}
>
<View style={[styles2.container, styles2.containerCentered]}>
<StatusBar hidden={true} />
<View style={styles2.subContainer}>
<Image
style={styles2.logo}
source={require("./assets/images/logo.png")}
/>
<ActivityIndicator size="large" color="#43300E" />
<Text>{i18n.t("app.loading") + "..."}</Text>
</View>
</View>
</ImageBackground>
);
}
}}
the 'isConnected' is on a file "myPreferences"
export async function userSessionActive() {
let userAuthorizationCode = await retrieveProfileAuthorizationCode();
let userUserId = await retrieveProfileUserId();
let userEmail = await retrieveProfileLogin();
let is_connected = false;
if (userAuthorizationCode != '' && userUserId !== null && parseInt(userUserId) > 0 && userEmail != '') {
is_connected = true;
}
return is_connected;
}
I thought doing something like this but it's not working :
function App(userSessionActive) {
const isConnected = userSessionActive.isConnected;
if (isConnected) {
return <NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>;
}
return <StackNavigator screenProps={'Authentication'}/>;
}
export default App

Children calling grandparent function

I have a box containing a list. The list is made of todoItems. A delete button is next to each item. The button should call the delete method of the box class. Should I pass it to the class List first? Can I call directly the method in the class Box?
class TodoItem extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(e)
{
const todoItemId = this.props.todoItemId;
if (!todoItemId)
{
return;
}
this.props.onTodoItemDeleteList({ todoItemId: todoItemId });
}
render() {
return (
<div className="todoItem">
<button onClick={() => this.handleClick()}>delete</button>;
</div>
);
}
}
My List: here the onTodoItemDeleteList is seen in the console, but appears as undefined.
class TodoItemList extends React.Component {
constructor(props) {
super(props);
this.handleItemDeleteList = this.handleItemDeleteList.bind(this);
}
handleItemDeleteList(todoItemId)
{
//call handleItemDelete
}
render() {
if (this.props.data)
{
var todoItemNodes = this.props.data.map(function (todoItem){
return (
<TodoItem todoItemId={todoItem.todoItemId} onTodoItemDeleteList={this.handleItemDeleteList} key={todoItem.todoItemId}>
</TodoItem>
);
});
}
return <div className="todoItemList">{todoItemNodes}</div>;
}
}
My Box: this is where I handle my ajax call to the server.
class TodoItemBox extends React.Component {
constructor(props) {
super(props);
this.state = { data: [] };
this.handleItemDelete = this.handleItemDelete.bind(this);
}
handleItemDelete(todoItemId) {
const data = new FormData();
data.append('todoItemId', todoItemId);
const xhr = new XMLHttpRequest();
xhr.open('post', this.props.deleteUrl, true);
xhr.onload = () => this.loadTodoItemsFromServer();
xhr.send(data);
}
render() {
return (
<div className="todoItemBox">
<TodoItemList data={this.state.data} />
</div>
);
}
}
I solved it by using arrow function in the parent too, it looks like this:
onTodoItemDeleteList={ (todoItemId) => handleItemDeleteList(todoItemId)}
and in the constructor:
handleItemDeleteList = this.handleItemDeleteList.bind(this);

react native - DatePickerIOS with AsyncStorage

I have tried storing DatePickerIOS dates with redux.
Is there a way to use AsyncStorage? I have been trying with no luck so far. Are there any suggestions on how to use AsyncStorage with a simple DatePickerIOS component?
<DatePickerIOS
style={{ paddingTop: 110 }}
mode='date'
date={this.state.d}
onDateChange={(d) => this.onDateChange(d)}
/>
constructor(props) {
this.state = { date: newDate() };
}
onDateChange(d) {
this.setState({
d: d
});
You set the state for the d variable in onDateChange but you use the startDate variable in the DatePickerIOS.
Take a look at this, didn't tested in app but should work.
export class PickerIOS extends React.Component {
constructor() {
super();
this.state = {
pickedDate: null
}
}
componentWillMount() {
getData('date')
.then((date) => {
if (date != null)
this.setState({pickedDate: date})
else
this.setState({pickedDate: new Date()})
})
}
onDateChange(date) {
setData('date', date)
this.setState({pickedDate: date})
}
render() {
return (
<DatePickerIOS
mode='date'
date={this.state.pickedDate}
onDateChange={(date) => this.onDateChange(date)}
/>
);
}
}
and then, for code organisation, in another file:
setData(key, data) {
try {
await AsyncStorage.setItem(key, data);
} catch (error) {
// Error saving data
}
}
getData(key) {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null){
return value
}
} catch (error) {
// Error retrieving data
}
}

React-Native pass Textinputvalue to other js

i'm a very newbie to react-native, so sry for this kind of question.
I have to implement a app with that i can log into our website. More details later.
First problem:
LoginScreen.js
var Animated = require('Animated');
var Dimensions = require('Dimensions');
var Image = require('Image');
var React = require('React');
var StatusBar = require('StatusBar');
var StyleSheet = require('StyleSheet');
var View = require('View');
var {
Text
} = require('OnTrackText');
var LoginButton = require('../common/LoginButton');
var TouchableOpacity = require('TouchableOpacity');
var TextInput = require('TextInput');
var {
skipLogin
} = require('../actions');
var {
connect
} = require('react-redux');
class LoginScreen extends React.Component {
state = {
anim: new Animated.Value(0),
name: '',
password: ''
};
componentDidMount() {
Animated.timing(this.state.anim, {
toValue: 3000,
duration: 3000
}).start();
}
render() {
return ( < Image style = {
styles.container
}
source = {
require('./img/login-background.png')
} >
< StatusBar barStyle = "default" / >
< TouchableOpacity accessibilityLabel = "Skip login"
accessibilityTraits = "button"
style = {
styles.skip
}
onPress = {
() => this.props.dispatch(skipLogin())
} >
< Animated.Image style = {
this.fadeIn(2800)
}
source = {
require('./img/x.png')
}
/>
</TouchableOpacity >
< View style = {
styles.section
} >
< Animated.Image style = {
this.fadeIn(0)
}
source = {
require('./img/ontrack-logo#3x.png')
}
/>
</View >
< View style = {
styles.section
} >
< Animated.Text style = {
[styles.h1, this.fadeIn(700, -20)]
} >
Willkommen zur < /Animated.Text>
<Animated.Text style={[styles.h1, {marginTop: -10}, this.fadeIn(700, 20)]}>
OnTrack App
</Animated.Text >
< /View>
<View style={styles.section}>
<TextInput
style={styles.input}
onChangeText={(text) => this.setState({ name: text }) }
value={this.state.name}
placeholder={"Benutzername"}
/ >
< TextInput style = {
styles.input
}
onChangeText = {
(text) => this.setState({
password: text
})
}
value = {
this.state.password
}
secureTextEntry = {
true
}
placeholder = {
"Password"
}
/>
</View >
< Animated.View style = {
[styles.section, styles.last, this.fadeIn(2500, 20)]
} >
< LoginButton name = {
this.state.name
}
password = {
this.state.password
}
source = "First screen" / >
< /Animated.View>
</Image >
);
}
fadeIn(delay, from = 0) {
....
}
const scale = Dimensions.get('window').width / 375;
var styles = StyleSheet.create({
....
}
});
module.exports = connect()(LoginScreen);
As you can see i would like to enter the name and password into the textinput.
Than
the LoginButton.js
'use strict';
const React = require('react');
const {StyleSheet} = require('react-native');
const { logInToWeb } = require('../actions');
const {connect} = require('react-redux');
class LoginButton extends React.Component {
props: {
style: any;
source?: string; // For Analytics
dispatch: (action: any) => Promise;
onLoggedIn: ?() => void;
};
state: {
isLoading: boolean;
};
_isMounted: boolean;
constructor() {
super();
this.state = { isLoading: false };
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
if (this.state.isLoading) {
return (
<OnTrackButton
style={[styles.button, this.props.style]}
caption="Please wait..."
onPress={() => {}}
/>
);
}
return (
<OnTrackButton
style={[styles.button, this.props.style]}
// icon={require('../login/img/f-logo.png')}
caption="Login to OnTrack"
// onPress={this.props.onpress}
onPress={() => this.logIn()}
/>
);
}
async logIn() {
const {dispatch, onLoggedIn, name, password} = this.props;
this.setState({isLoading: true});
try {
await Promise.race([
dispatch(logInToWeb(name,password)),
timeout(15000),
]);
} catch (e) {
const message = e.message || e;
if (message !== 'Timed out' && message !== 'Canceled by user') {
alert(message);
console.warn(e);
}
return;
} finally {
this._isMounted && this.setState({isLoading: false});
}
onLoggedIn && onLoggedIn();
}
}
async function timeout(ms: number): Promise {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Timed out')), ms);
});
}
var styles = StyleSheet.create({
...
});
module.exports = connect()(LoginButton);
Than
the dispatch(logInToWeb)method in ./action/login.js looks like this:
'use strict';
import type { Action, ThunkAction } from './types';
const Parse = require('parse/react-native');
const {Platform} = require('react-native');
const Alert = require('Alert');
function logInToWeb(data): ThunkAction {
const {name, password} = data
Alert.alert(
`Hi, ${name} & ${password}`,
'möchten Sie sich ausloggen?',
[
{ text: 'Abbruch' },
{ text: 'Ausloggen' },
]
)
}
function skipLogin(): Action {
return {
type: 'SKIPPED_LOGIN',
};
}
function logOut(): ThunkAction {
...
}
function logOutWithPrompt(): ThunkAction {
....
}
module.exports = {logInToWeb, skipLogin, logOut, logOutWithPrompt};
So the Question is:
how can i pass the value of the Textinput from the LoginScreen.js on ButtonClick To the logInToWeb-Method in the login.js
How can i get the name and password in the alert that i called in login.js
Thats it. Later i will ask more about bearer-auth and loggin to server :)
I think what you're asking is how to send the name and password to your logIn() method? Maybe something like this would work:
// Login Button
<LoginButton
name={this.state.name}
password={this.state.password}
source="First screen" />
// Login method
async logIn() {
const {dispatch, onLoggedIn, name, password} = this.props;
this.setState({isLoading: true});
try {
await Promise.race([
dispatch(logInToWebk({name, password})),
timeout(15000),
]);
}
}
then
function logInToWebk(data): ThunkAction {
const { name, password } = data
// do something with name + password
}