admin管理员组文章数量:1134041
I want to use firebase auth with react native for Login
and Signup
but I got a yellow error:
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See () for more info. (Saw setTimeout with duration 111862ms)
How Can I Fix That?
I don't want to ignore that, I want to understand this error and solve that with the best and Standard way.
And This is my Code:
export default class Login extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
response: ''
}
this.signUp = this.signUp.bind(this)
this.login = this.login.bind(this)
}
async signUp() {
try {
await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
this.setState({
response: 'Account Created!'
})
setTimeout(() => {
this.props.navigator.push({
id: 'App'
})
}, 1500)
} catch (error) {
this.setState({
response: error.toString()
})
}
}
async login() {
try {
await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
this.setState({
response: 'user login in'
})
setTimeout(() => {
this.props.navigator.push({
id: 'App'
})
})
} catch (error) {
this.setState({
response: error.toString()
})
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.containerInputes}>
<TextInput
placeholderTextColor="gray"
placeholder="Email"
style={styles.inputText}
// onChangeText={(email) => this.setState({ email })}
onChangeText={(email) => {console.log(email);}}
/>
<TextInput
placeholderTextColor="gray"
placeholder="Password"
style={styles.inputText}
password={true}
onChangeText={(password) => this.setState({ password })}
/>
</View>
<TouchableHighlight
onPress={this.login}
style={[styles.loginButton, styles.button]}
>
<Text
style={styles.textButton}
>Login</Text>
</TouchableHighlight>
<TouchableHighlight
onPress={this.signUp}
style={[styles.loginButton, styles.button]}
>
<Text
style={styles.textButton}
>Signup</Text>
</TouchableHighlight>
</View>
)
}
}
I Reported to Google Firebase Team: ()
I want to use firebase auth with react native for Login
and Signup
but I got a yellow error:
Setting a timer for a long period of time, i.e. multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called when the app is in the foreground. See (https://github.com/facebook/react-native/issues/12981) for more info. (Saw setTimeout with duration 111862ms)
How Can I Fix That?
I don't want to ignore that, I want to understand this error and solve that with the best and Standard way.
And This is my Code:
export default class Login extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
response: ''
}
this.signUp = this.signUp.bind(this)
this.login = this.login.bind(this)
}
async signUp() {
try {
await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
this.setState({
response: 'Account Created!'
})
setTimeout(() => {
this.props.navigator.push({
id: 'App'
})
}, 1500)
} catch (error) {
this.setState({
response: error.toString()
})
}
}
async login() {
try {
await firebase.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
this.setState({
response: 'user login in'
})
setTimeout(() => {
this.props.navigator.push({
id: 'App'
})
})
} catch (error) {
this.setState({
response: error.toString()
})
}
}
render() {
return (
<View style={styles.container}>
<View style={styles.containerInputes}>
<TextInput
placeholderTextColor="gray"
placeholder="Email"
style={styles.inputText}
// onChangeText={(email) => this.setState({ email })}
onChangeText={(email) => {console.log(email);}}
/>
<TextInput
placeholderTextColor="gray"
placeholder="Password"
style={styles.inputText}
password={true}
onChangeText={(password) => this.setState({ password })}
/>
</View>
<TouchableHighlight
onPress={this.login}
style={[styles.loginButton, styles.button]}
>
<Text
style={styles.textButton}
>Login</Text>
</TouchableHighlight>
<TouchableHighlight
onPress={this.signUp}
style={[styles.loginButton, styles.button]}
>
<Text
style={styles.textButton}
>Signup</Text>
</TouchableHighlight>
</View>
)
}
}
I Reported to Google Firebase Team: (https://github.com/firebase/firebase-js-sdk/issues/97)
Share Improve this question edited Jul 15, 2017 at 10:01 Saeed Heidarizarei asked Jun 17, 2017 at 10:15 Saeed HeidarizareiSaeed Heidarizarei 8,90622 gold badges65 silver badges111 bronze badges15 Answers
Reset to default 66To fix this issue...
Navigate to your
node_modules/react-native/Libraries/Core/Timers/JSTimers.js
file.Look for the variable:
MAX_TIMER_DURATION_MS
Change its value to:
10000 * 1000
Save the changes (with auto format turned off) and re-build your app.
Found this answer on https://github.com/firebase/firebase-js-sdk/issues/97#issuecomment-485410026
Just add these two lines
import { LogBox } from 'react-native';
LogBox.ignoreLogs(['Setting a timer']);
Old
This fixes the yellow box and the console log. It even fixes it for Expo.
Simply place the following script at the beginning of your codebase.
import { YellowBox } from 'react-native';
import _ from 'lodash';
YellowBox.ignoreWarnings(['Setting a timer']);
const _console = _.clone(console);
console.warn = message => {
if (message.indexOf('Setting a timer') <= -1) {
_console.warn(message);
}
};
New
YellowBox
is deprecated and is replaced byLogBox
import { LogBox } from 'react-native';
import _ from 'lodash';
LogBox.ignoreLogs(['Warning:...']); // ignore specific logs
LogBox.ignoreAllLogs(); // ignore all logs
const _console = _.clone(console);
console.warn = message => {
if (message.indexOf('Setting a timer') <= -1) {
_console.warn(message);
}
};
Josh Crowther Software Engineer at Google Said:
Using multi-step short duration setTimeouts doesn't actually fix the problem though. The Timer module still stays active and the app is still subject to the performance issues indicated in the warning. The issue here is that, we have use cases that require long timers, and react-native doesn't optimize for that use case.
So the net of all that is: This bug can't be fixed here we can only work around the error there are workarounds available (see Setting a timer for a long period of time, i.e. multiple minutes) that disable the warning. Doing the work to disable the warning in our code, doesn't help the issue (beyond disabling the warning), and adds additional SDK code/weight that is completely unnecessary.
I'd recommend chiming in on the issue mentioned above (i.e. facebook/react-native#12981) if you aren't comfortable w/ the workaround provided
If you are using react-native 0.63
then do the followings in your App.js
file:
import the LogBox
from react-native
import { LogBox } from 'react-native';
and after all the imports in your App.js
file just add this line, it is not necessary to add this line in any useEffect call.
LogBox.ignoreLogs(['Setting a timer for a long period of time'])
See the docs to learn more.
OR
If you are using react-native 0.62
then do the followings in your App.js
file:
import the YellowBox
from react-native
import { YellowBox } from 'react-native';
and after all the imports in your App.js
file just add this line, it is not necessary to add this line in any useEffect call.
YellowBox.ignoreWarnings(['Setting a timer for a long period of time']);
See the docs to learn more.
Work around issue with yellow warning 'Setting a timer' .
copy & import following file (as fast as you can ;-))
import {Platform, InteractionManager} from 'react-native';
const _setTimeout = global.setTimeout;
const _clearTimeout = global.clearTimeout;
const MAX_TIMER_DURATION_MS = 60 * 1000;
if (Platform.OS === 'android') {
// Work around issue `Setting a timer for long time`
// see: https://github.com/firebase/firebase-js-sdk/issues/97
const timerFix = {};
const runTask = (id, fn, ttl, args) => {
const waitingTime = ttl - Date.now();
if (waitingTime <= 1) {
InteractionManager.runAfterInteractions(() => {
if (!timerFix[id]) {
return;
}
delete timerFix[id];
fn(...args);
});
return;
}
const afterTime = Math.min(waitingTime, MAX_TIMER_DURATION_MS);
timerFix[id] = _setTimeout(() => runTask(id, fn, ttl, args), afterTime);
};
global.setTimeout = (fn, time, ...args) => {
if (MAX_TIMER_DURATION_MS < time) {
const ttl = Date.now() + time;
const id = '_lt_' + Object.keys(timerFix).length;
runTask(id, fn, ttl, args);
return id;
}
return _setTimeout(fn, time, ...args);
};
global.clearTimeout = id => {
if (typeof id === 'string' && id.startsWith('_lt_')) {
_clearTimeout(timerFix[id]);
delete timerFix[id];
return;
}
_clearTimeout(id);
};
}
import { YellowBox } from 'react-native';
construct() {
YellowBox.ignoreWarnings(['Setting a timer']);
}
This ignores the warning for me. You should add this to the constructor of every page that shows the warning.
Ignoring it is not the best approach, but if you're using Firebase Realtime Database. They are looking into solving this issue with their library, even though the issue is 2 years old.
What I did and it's working with me but still I don't knwo if it's a good practice or not
Navigated to file
node_modules\react-native\Libraries\Core\Timers\JSTimers.js
there is a function const MAX_TIMER_DURATION_MS = 60 * 1000
and I increased the time to be 60 * 100000 and it stopeed appearing
I got the same issue and I think it's problem of firebase web SDK so I decided to drop firebase web SDK because it runs in js thread, not react-native thread.
And I found react-native-firebase . It's better than firebase web SDK with higher performance and this issue went away
Facing the same issue.. Seems like, we'd have to hide the warning for the time being. Here's the shortest way to do it:
componentDidMount() { console.disableYellowBox = true; ... }
For firebase/firestore users: Rather than trying to mask / hide the warning, I simply reverted to the REST API. Works like a charm :)
- I'm using the
firebase
node package for auth - I'm using regular HTTP calls for firestore (which also gets rid of the otherwise necessary
atob
andbto
hacks)
Snippet below just uses an HTTP client (https://github.com/hardcodet/httpclient-js), you can use whatever works for you (e.g. fetch or axios).
// Firebase App (the core Firebase SDK) is always required and
// must be listed before other Firebase SDKs
import * as firebase from "firebase/app";
import "firebase/auth";
import {LoginStatus} from "./LoginStatus";
import {auth, User} from "firebase";
import {ApiResponse, DelegateBearerAuthClient, HttpClient} from "@hardcodet/httpclient";
import {Env} from "../../Env";
export class FirebaseClient {
/**
* Creates a simple API client to use with Firestore.
* We don't want to use the JS package's implementation since it has issues with
* long-running timers - see https://github.com/firebase/firebase-js-sdk/issues/97
* @param idToken The user's ID token as retrieved through firebase auth
*/
private static getFirestoreClient(idToken: string) {
const projectId = Env.firebaseProjectId;
const baseUri = `https://firestore.googleapis.com/v1/projects/${projectId}/databases/(default)/documents/`
const authClient = new DelegateBearerAuthClient(async () => idToken);
return new HttpClient(baseUri, {authClient});
}
/**
* Use firebase auth for login etc. because lazy.
*/
public static async userLogin(email: string, password: string): Promise<User> {
try {
const credentials: firebase.auth.UserCredential = await auth().signInWithEmailAndPassword(email, password);
return credentials.user;
} catch (error) {
console.error(error);
return undefined;
}
}
private static resolveStatus(errorCode: string): { user: User, status: LoginStatus } {
switch (errorCode) {
case "auth/invalid-email":
return {user: undefined, status: LoginStatus.InvalidEmailAddress};
case "auth/user-not-found":
return {user: undefined, status: LoginStatus.UnknownUserId};
case "auth/wrong-password":
return {user: undefined, status: LoginStatus.WrongPassword};
case "auth/email-already-in-use":
return {user: undefined, status: LoginStatus.EmailAddressAlreadyInUse};
case "auth/weak-password":
return {user: undefined, status: LoginStatus.WeakPassword};
case "auth/user-disabled":
return {user: undefined, status: LoginStatus.UserDisabled};
case "auth/expired-action-code":
return {user: undefined, status: LoginStatus.InvalidActionCode};
default:
return {user: undefined, status: LoginStatus.Undefined};
}
}
/**
* Resolve the user's keys from the backend.
*/
public static async getSomeUserData(firebaseUserId: string, idToken: string): Promise<...> {
const client: HttpClient = FirebaseClient.getFirestoreClient(idToken);
// userData here is the name of my collection in firestore. i'm using the user ID as the document ID
var result = await client.getAs<any>(`userData/${firebaseUserId}?key=${Env.firebaseApiKey}`);
if (result.success) {
const json = result.value;
const foo = json.fields.foo.stringValue;
const bar = json.fields.bar.stringValue;
return ...
} else {
if (result.notFound) {
// that document with that key doesn't exist
console.warn("no document with key " + firebaseUserId);
return undefined;
}
throw result.createError();
}
}
public static async writeSomeData(idToken: string, firebaseUserId: string, foo: string, bar: string): Promise<...> {
const data = {
"fields": {
"foo": {
"stringValue": foo
},
"bar": {
"stringValue": bar
}
}
};
// again, just do an HTTP post, use the firebase user ID as the document key
const client: HttpClient = FirebaseClient.getFirestoreClient(idToken);
const result: ApiResponse = await client.post(`userData?documentId=${firebaseUserId}&key=${Env.firebaseApiKey}`, data);
if (result.success) {
return ...
} else {
throw result.createError();
}
}
/**
* Gets the currently logged in user, if any.
*/
public static getCurrentUser(): User {
return auth().currentUser;
}
public static async sendPasswordReset(email: string): Promise<LoginStatus> {
try {
await auth().sendPasswordResetEmail(email);
return LoginStatus.Success;
} catch (error) {
return FirebaseClient.resolveStatus(error.code).status;
}
}
/**
* Clears the current user from the session.
*/
public static async signOut() {
await auth().signOut();
}
}
Note the idToken
parameter - this is simply the ID token you get off the firebase User
class:
const user: User = await FirebaseClient.userLogin("[email protected]", "mypassword");
const idToken= await user.getIdToken(false);
const data = await FirebaseClient.getSomeUserData(user.uid, idToken);
In login()
, your setTimeout()
call is missing an interval value. As a general, browsers now do not fire timeouts/intervals if the window/tab is in the background, or at least they are not expected to be timely. This is to prevent abusive script behaviour, and to reduce power consumption by scripts that may be polling.
Your code should work in principle, if the user switches away from the window whilst the timer is running, it will complete when they return. This is probably what you want from a UX point of view, because the users sees the transition, rather than it happening in the background when they are not looking. It helps them maintain mental context.
The yellow box is because you are setting an excessively long timer according to the message (nearly two minutes) and that is unlikely to be contextually what you want. The JS environment is warning you that what you are doing it not likely what you intend. You can mute the warning if it is what you want.
I think the most close-aide solution to this problem (until RN fixed this internally) is the solution mentioned here.
// add this code to your project to reset all timeouts
const highestTimeoutId = setTimeout(() => ';');
for (let i = 0; i < highestTimeoutId; i++) {
clearTimeout(i);
}
Being used that - against Firebase call I still receive one yellow box warning (on settimeout), but any concurrent warnings never come thereafter. I'm not sure calling this at which point may not trigger the only warning also, but not any concurrent warning throws anymore after Firebase's async
calls.
Another approach is to use the react-native-ignore-warnings module.
https://github.com/jamrizzi/react-native-ignore-warnings
https://www.npmjs.com/package/react-native-ignore-warnings
This even works for Expo apps.
You would use it the following way . . .
import ignoreWarnings from 'react-native-ignore-warnings';
ignoreWarnings('Setting a timer');
can run with android too..?
I'm disabling the warning with this code
import { YellowBox } from 'react-native';
YellowBox.ignoreWarnings(['Setting a timer']);
本文标签: javascriptSetting a timer for a long period of timeie multiple minutesStack Overflow
版权声明:本文标题:javascript - Setting a timer for a long period of time, i.e. multiple minutes - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736799784a1953438.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论