【问题标题】:React native text not updating反应原生文本不更新
【发布时间】:2021-05-02 09:02:44
【问题描述】:

我试图在 React Native 应用程序中模拟呼吸,呼吸的控制台日志似乎可以正常工作,但是屏幕上的文本永远不会更新。它只是坐在“IN”上

我是新来的反应和努力寻找解决方案。我尝试使用 useState 代替 useRef 来获取当前的呼吸状态,但它仍然没有更新,并且在使用 useState 时控制台无法按预期工作。

useRef 版本

import React from 'react'
import { useState, useRef, useEffect } from 'react'
import { SafeAreaView } from 'react-native'
import { Layout, Text, Button } from '@ui-kitten/components'

const HomeScreen = () => {

    const [sessionStarted, setSessionStarted] = useState(false)
    const breathDoing = useRef('STOP');


    const doNext = () => {
        console.log('doNext')
        switch(breathDoing.current) {
            case 'IN':
                console.log('We are breathing in we should breath out')
                breathOut()
                break
            case 'OUT':
                console.log('We are breathing out we should breath in')
                breathIn()
                break
            case 'STOP':
                console.log('We should stop breathing, yikes')
                break
        }
    }
    
    const breathIn = () => {
        console.log('In breath in')
        breathDoing.current = 'IN'
        setTimeout(() => {
            doNext()
        }, 1000)
    }

    const breathOut = () => {
        console.log('In breath out')
        breathDoing.current = 'OUT'
        setTimeout(() => {
            doNext()
        }, 1000)
    }

    const breathSession = () => {
        console.log('breathSession');
        setSessionStarted(true)
        breathIn()
    }

    const stopBreathSession = () => {
        console.log('stopBreathSession');
        setSessionStarted(false)
        breathDoing.current = 'STOP'
    }

    const BreathOutput = (props) => {
        return <Text>{props.doing}</Text>
    }

    const handleSessionButton = () => {
        if (sessionStarted) {
            stopBreathSession()
        } else {
            breathSession()
        }
    }

    return (
        <>
            <SafeAreaView>
                <Layout level="1">
                    {sessionStarted ? 
                    <>
                       <BreathOutput doing={breathDoing.current} />
                    </>
                    : null}
                    <Button onPress={() => handleSessionButton()}>
                        {sessionStarted ? 'stop' : 'start'}
                    </Button>
                </Layout>
            </SafeAreaView>
        </>
    )
}

export default HomeScreen

控制台显示正确的输出:

We are breathing in we should breath out
In breath out
doNext
We are breathing out we should breath in
In breath in
doNext
We are breathing in we should breath out
In breath out

编辑

useState 版本:

    const [sessionStarted, setSessionStarted] = useState(false)
    const [breathDoing, setBreathDoing] = useState('STOP')
    
    const doNext = () => {
        console.log('doNext')
        switch(breathDoing) {
            case 'IN':
                console.log('We are breathing in we should breath out')
                breathOut()
                break
            case 'OUT':
                console.log('We are breathing out we should breath in')
                breathIn()
                break
            case 'STOP':
                console.log('We should stop breathing, yikes')
                break
        }
    }
    
    const breathIn = () => {
        console.log('In breath in')
        setBreathDoing('IN')
        setTimeout(() => {
            doNext()
        }, 1000)
    }

    const breathOut = () => {
        console.log('In breath out')
        setBreathDoing('OUT')
        setTimeout(() => {
            doNext()
        }, 1000)
    }

    const breathSession = () => {
        console.log('breathSession');
        setSessionStarted(true)
        breathIn()
    }

    const stopBreathSession = () => {
        console.log('stopBreathSession');
        setSessionStarted(false)
        setBreathDoing('STOP')
    }

    const BreathOutput = (props) => {
        return <Text>{props.doing}</Text>
    }

    const handleSessionButton = () => {
        if (sessionStarted) {
            stopBreathSession()
        } else {
            breathSession()
        }
    }

    return (
        <>
            <SafeAreaView>
                <Layout level="1">
                    {sessionStarted ? 
                    <>
                       <BreathOutput doing={breathDoing} />
                    </>
                    : null}
                    <Button onPress={() => handleSessionButton()}>
                        {sessionStarted ? 'stop' : 'start'}
                    </Button>
                </Layout>
            </SafeAreaView>
        </>
    ) 

文本保持为“IN”

控制台输出

breathSession
In breath in
doNext
We should stop breathing, yikes

【问题讨论】:

  • 每当breathDoing.current 的值发生变化时,react 不会重新渲染组件,因此值将保持不变。要解决此问题,我建议使用 useState 而不是 useRef
  • 如果在返回前加上console.log(breathDoing);,输出是什么?它是否正确地将值传递给 BreathOutput?

标签: reactjs react-native


【解决方案1】:

setTimeout 调用缓存状态值,因此这些值永远不会更新。另一种方法是使用 useEffect 挂钩监听状态变化:

export default function App() {
  const [sessionStarted, setSessionStarted] = useState(false);
  const [breathDoing, setBreathDoing] = useState("STOP");

  const getLabel = (status) => {
    switch (status) {
      case "IN":
        return "We are breathing in we should breath out";
      case "OUT":
        return "We are breathing out we should breath in";
      case "STOP":
        return "We should stop breathing, yikes";
    }
  };

  useEffect(() => {
    const stateTransitions = {
      STOP: "IN",
      IN: "OUT",
      OUT: "IN"
    };

    if (sessionStarted) {
      const nextState = stateTransitions[breathDoing];

      setTimeout(() => {
        setBreathDoing(nextState);
      }, 1000);
    }
  }, [sessionStarted, breathDoing]);

  const breathSession = () => {
    console.log("breathSession");
    setBreathDoing("IN");
    setSessionStarted(true);
  };

  const stopBreathSession = () => {
    console.log("stopBreathSession");
    setSessionStarted(false);
    setBreathDoing("STOP");
  };

  const BreathOutput = (props) => {
    return <div>{props.doing}</div>;
  };

  const handleSessionButton = () => {
    if (sessionStarted) {
      stopBreathSession();
    } else {
      breathSession();
    }
  };

  return (
    <>
      <div>
        <div level="1">
          {sessionStarted ? (
            <>
              {getLabel(breathDoing)}
              <BreathOutput doing={breathDoing} />
            </>
          ) : null}
          <button onClick={() => handleSessionButton()}>
            {sessionStarted ? "stop" : "start"}
          </button>
        </div>
      </div>
    </>
  );
}

背景资料:React - useState - why setTimeout function does not have latest state value?

【讨论】:

  • 嘿,谢谢这个作品。有趣的是,它在 ios 模拟器上不起作用,它会暂停并等待我单击屏幕上的任意位置以推进状态。但是我后来在 iphone 上试了一下,它成功了。
  • 很高兴听到!您可能需要调整过渡时间,但我认为这应该作为一个起点。
【解决方案2】:

你应该使用 useState() 而不是 useRef()。要初始化呼吸状态,您可以使用如下语句

const [breathingState, setBreathingState] = useState('STOP');

然后,从您的函数 BreathIn() 或 BreathOut() 中,像这样更新呼吸状态:

setBreathingState('IN');

setBreathingState('OUT')

分别。

在 JSX 中,只需要像下面这样引用呼吸状态:

<BreathOutput doing={breathingState} />

React 应该意识到需要重新渲染。

【讨论】:

  • 谢谢,这是我最初的 useState 尝试,它也没有工作。我认为它与 setTimeout 闭包有关,它保存旧状态而不是访问新状态,我添加了原始尝试的编辑
猜你喜欢
  • 1970-01-01
  • 2019-06-05
  • 2020-12-19
  • 1970-01-01
  • 2020-01-30
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 2022-11-15
相关资源
最近更新 更多