太多的重新渲染。 React 限制渲染次数以防止无限循环。使用上下文传递参数 API

Too many re-renders. React limits the number of renders to prevent an infinite loop. Passing parameters with context API

我正在使用 React context 获取 context provider 文件中定义的登录函数和错误以登录到 firebase 数据库,并使用登录时抛出的错误在应用程序中显示它。

我的问题是,当我只是使用从 firebase 身份验证抛出的错误来显示应用程序中的错误时,我没有收到任何错误,但我还添加了其他错误,如空字段,以在中显示错误字段应用程序。

这样做之后我得到了太多重新渲染的错误。我认为这个错误是由于在函数中使用了多个 if 语句。你能建议一个新的选项吗?如果这是错误的,请解释一下。

上下文提供程序文件

import React from 'react';
import auth from '@react-native-firebase/auth';

export const AuthContext = React.createContext();

export const AuthProvider = ({children}) => {
  const [user, setUser] = React.useState(null);
  const [error, setError] = React.useState('');

  return (
    <AuthContext.Provider
      value={{
        user,
        setUser,
        error,
        login: async (email, pwd) => {
          try {
            await auth().signInWithEmailAndPassword(email, pwd);
          } catch (e) {
            setError(e);
          }
        },
        register: async (email, pwd) => {
          try {
            await auth().createUserWithEmailAndPassword(email, pwd);
          } catch (e) {
            setError(e);
          }
        },
        logout: async () => {
          try {
            await auth().signOut();
          } catch (e) {
            console.log(e);
          }
        },
      }}>
      {children}
    </AuthContext.Provider>
  );
};

应用文件

import React from 'react';
import {Button, Image, Text, TouchableOpacity, View} from 'react-native';
import styles from '../styles/Styles';
import FormInput from '../components/FormInput';
import FormButton from '../components/FormButton';
import SocialButton from '../components/SocialButton';
import {AuthContext} from '../navigation/AuthProvider';
import ErrorText from '../components/ErrorText';

const LoginScreen = ({navigation}) => {
  const [email, setEmail] = React.useState('');
  const [password, setPassword] = React.useState('');

  const {login, error} = React.useContext(AuthContext);
  const [errorForwarded, setErrorForwarded] = React.useState(null);

  if (error) {
    setErrorForwarded(error);
  }

  if (!email || !password) {
    setErrorForwarded('fields-empty');
  }

  const renderErrorText = e => {
    if (e.code === 'auth/invalid-email') {
      return <ErrorText errorText="Email invalid!" />;
    }
    if (e.code === 'auth/user-not-found') {
      return <ErrorText errorText="User not found!" />;
    }
    if (e.code === 'auth/wrong-password') {
      return <ErrorText errorText="Wrong password!" />;
    }
    if (e === 'fields-empty') {
      return <ErrorText errorText="Fields cannot be empty!" />;
    }
  };

  return (
    <View style={styles.loginContainer}>
      <Image
        source={require('../assets/rn-social-logo.png')}
        style={styles.loginLogo}
      />
      <Text style={styles.loginText}>Sign In</Text>
      <FormInput
        labelValue={email}
        onChangeText={email => setEmail(email)}
        placeholderText="Email"
        iconType="user"
        keyboardType="email-address"
        autoCapitalize="none"
        autoCorrect={false}
      />
      <FormInput
        labelValue={password}
        onChangeText={pwd => setPassword(pwd)}
        placeholderText="Password"
        iconType="lock"
        secureTextEntry={true}
      />
      {error ? renderErrorText(errorForwarded) : null}
      <FormButton
        buttonTitle="Sign In"
        onPress={() => {
          {
            email && password ? login(email, password) : {};
          }
        }}
      />
      <TouchableOpacity style={styles.loginForgetBtn}>
        <Text style={styles.loginNavBtnText}>Forgot Password?</Text>
      </TouchableOpacity>
      <SocialButton
        buttonTitle="Sign In with Facebook"
        buttonType="facebook-square"
        color="#4867aa"
        backgroundColor="#e6eaf4"
      />
      <SocialButton
        buttonTitle="Sign In with Google"
        buttonType="google"
        color="#de4d41"
        backgroundColor="#f5e7ea"
      />
      <TouchableOpacity
        style={styles.loginForgetBtn}
        onPress={() => navigation.navigate('Signup')}>
        <Text style={styles.loginNavBtnText}>
          Don't have an account? Create here...
        </Text>
      </TouchableOpacity>
    </View>
  );
};

export default LoginScreen;

您遇到多次重新呈现的原因与函数中的 if 条件无关。通过 运行 直接在组件函数内设置状态的函数,您将强制重新渲染,然后它开始从上到下 运行 函数,再次设置状态。

只有 React 钩子不是 reinitialized/run 并且可以在每次后续重新渲染时避免 运行ning,因此您需要使用带有依赖项数组的 useEffect() 钩子来注意,这样您就不会 运行 在重新渲染时发生不必要的状态更改。

我将包含对代码相关位的更改(在 App 组件的 return 语句之前)

const [email, setEmail] = React.useState('');
const [password, setPassword] = React.useState('');

const {login, error} = React.useContext(AuthContext);
const [errorForwarded, setErrorForwarded] = React.useState(null);

React.useEffect(() => {
  if (error) {
    setErrorForwarded(error);
  }
}, [error]) //using an array of dependencies like this ensures that the function only runs on changes in the error state

React.useEffect(() => {
  if (!email || !password) {
    setErrorForwarded('fields-empty');
  }
}, [email, password]) //similarly, only run this hook for changes in email or password

谢谢大家,我知道导致重新渲染的问题是直接在函数组件中的多个 if 语句,我只是想不出如何克服它。

当我使用一个函数来检查那些 if 条件以设置这些状态并在调用 API 之前在按钮提交中调用该函数时,它起作用了。但我认为 useEffect hook 做得更好,谢谢大家的澄清。

我能澄清一下吗,一开始错误变量是空的,所以 errorForwarded 将被设置为空。现在再次重新渲染时,errorForwarded 将被设置为空。所以我的问题是,即使检测到状态变化,实际状态值也没有改变。那为什么要重新渲染发生。谢谢。