преобразовать компонент на основе классов в компонент на основе функций - PullRequest
0 голосов
/ 07 мая 2020

ребята, я хочу преобразовать этот код:

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = { isLoading: true };
  }

  performTimeConsumingTask = async () => {
    return new Promise((resolve) =>
      setTimeout(() => {
        resolve('result');
      }, 2000)
    );
  };

  async componentDidMount() {
    const data = await this.performTimeConsumingTask();
    if (data !== null) this.setState({ isLoading: false });
  }

  render() {

    if (this.state.isLoading) return <SplashScreen />;

    const { state, navigate } = this.props.navigation;

    return (something)

Я написал этот код, но он не работает:

const App = () => {
  const [fontLoaded, setFontLoaded] = useState(false);
  const [isTimerOn, setIsTimerOn] = useState(true);

  if (!fontLoaded) {
    return (
      <AppLoading
        startAsync={fetchFonts}
        onFinish={() => setFontLoaded(true)}
      />
    );
  }
  useEffect(async () => {
    const data = await performTimeConsumingTask();
    if (data !== null) setIsTimerOn(false);
  });
  if (isTimerOn) return <SplashScreen />;
  else {
    return (something)

Это покажет ошибку: Invariant Violation: Rendered More Hooks than during the previous render.

Если я прокомментирую хук useEffect, он запустит splashScreen. Может ли кто-нибудь помочь мне преобразовать его?

Ответы [ 2 ]

0 голосов
/ 07 мая 2020

Передайте [] в качестве аргумента, если вы хотите использовать этот крючок как componentDidMount

useEffect(async () => {
const data = await performTimeConsumingTask();
if (data !== null) setIsTimerOn(false);
}, []); 

Вот список хуков, с помощью которых вы можете использовать хуки для замены методов жизненного цикла

https://medium.com/javascript-in-plain-english/lifecycle-methods-substitute-with-react-hooks-b173073052a

Причина появления ошибки в том, что ваш компонент выполняет рендеринг слишком много раз, а useEffect также выполняется при каждом рендеринге, при передаче [] будет запущен useEffect при первом рендеринге, так как он будет вести себя как componentDidMount.

Также следуйте этому, чтобы выполнять сетевые вызовы внутри useEffect

https://medium.com/javascript-in-plain-english/handling-api-calls-using-async-await-in-useeffect-hook-990fb4ae423

0 голосов
/ 07 мая 2020

Перед использованием всех хуков не должно быть условного возврата, в вашем случае вы возвращаете перед использованием useEffect.

Также useEffect не должен запускаться при каждом рендеринге, поскольку он устанавливает состояние в вашем случае. Поскольку вы хотите, чтобы он запускался только при начальном рендеринге, передайте пустой массив в качестве второго аргумента.

Также функция обратного вызова useEffect не может быть асинхронной c.

Подробнее о useEffect в документации.

Проверьте обновленный код ниже

const App = () => {
  const [fontLoaded, setFontLoaded] = useState(false);
  const [isTimerOn, setIsTimerOn] = useState(true);

  const performTimeConsumingTask = async () => {
      return new Promise((resolve) =>
         setTimeout(() => {
           resolve('result');
        }, 2000)
   );
  };
  useEffect(() => {
    async function myFunction() {
       const data = await performTimeConsumingTask();
       if (data !== null) setIsTimerOn(false);
    }
    myFunction();
  }, []); // With empty dependency it runs on initial render only like componentDidMount

  if (!fontLoaded) {
    return (
      <AppLoading
        startAsync={fetchFonts}
        onFinish={() => setFontLoaded(true)}
      />
    );
  }
  if (isTimerOn) return <SplashScreen />;
  else {
    return (something)
...