简体   繁体   English

在 React 中逐个字母地“打印”字符串

[英]"Print" string letter by letter in React

I have a React Native component that needs to show an Animation with the letters of a string printing letter by letter.我有一个 React Native 组件,它需要显示一个 Animation,其中一个字符串的字母一个字母一个字母地打印。 So, logically, I need to update a React state hook inside a loop appending each character of the string to it in a 1 second interval.因此,从逻辑上讲,我需要更新一个循环内的 React state 挂钩,以 1 秒的间隔将字符串的每个字符附加到它。 What I have so far is:到目前为止我所拥有的是:

let [placeholder, change_placeholder] = React.useState('');
const placeholder_print = () => {
    let temp = "Search for anything"
    console.log("Value of temp is now: " + temp)
    console.log("Length of string: " + temp.length)
    for (let i = 0; i < temp.length; i++) {
        console.log("Selected character is: " + temp.charAt(i))
        change_placeholder(placeholder.concat(temp.charAt(i)))
        console.log("Placeholder is now: " + placeholder)
    }
}

and the React component with the state hook in it:以及带有 state 钩子的 React 组件:

<View>
    <Text>{placeholder}</Text>
</View>

Yes, I know that this won't animate.是的,我知道这不会动画。 But in the end of the for , the value of placeholder should logically be the string itself, right?但是在for的最后, placeholder的值在逻辑上应该是字符串本身,对吧? If so, I might be able to get around the animation part with setTimeout() inside the for loop to run this every second.如果是这样,我可以绕过 animation 部分,在 for 循环中使用setTimeout()每秒运行一次。 But, this is the output when placeholder_print() is run:但是,这是运行placeholder_print()时的 output:

[Fri Jan 15 2021 16:29:30.166]  LOG      Value of temp is now: Search
[Fri Jan 15 2021 16:29:30.168]  LOG      Length of string: 6
[Fri Jan 15 2021 16:29:30.169]  LOG      Selected character is: S
[Fri Jan 15 2021 16:29:30.170]  LOG      Placeholder is now:
[Fri Jan 15 2021 16:29:30.170]  LOG      Selected character is: e
[Fri Jan 15 2021 16:29:30.170]  LOG      Placeholder is now:
[Fri Jan 15 2021 16:29:30.171]  LOG      Selected character is: a
[Fri Jan 15 2021 16:29:30.171]  LOG      Placeholder is now:
[Fri Jan 15 2021 16:29:30.171]  LOG      Selected character is: r
[Fri Jan 15 2021 16:29:30.172]  LOG      Placeholder is now:
[Fri Jan 15 2021 16:29:30.172]  LOG      Selected character is: c
[Fri Jan 15 2021 16:29:30.172]  LOG      Placeholder is now:
[Fri Jan 15 2021 16:29:30.173]  LOG      Selected character is: h
[Fri Jan 15 2021 16:29:30.173]  LOG      Placeholder is now:

When running a similar logic in Python or even vanilla JavaScript with no state hooks, this works fine.当在 Python 或什至没有 state 挂钩的香草 JavaScript 中运行类似的逻辑时,这工作正常。 I don't know if this needs to be implemented with the <Animated> component in React Native, or if it's some fundamental difference in the way React state hooks work.我不知道这是否需要使用 React Native 中的<Animated>组件来实现,或者这是否是 React state 挂钩工作方式的根本区别。 I'm stuck here and helping hands will be upvoted.我被困在这里,帮助之手将得到支持。

The reason you aren't seeing the update in your for loop is that setState() is asynchronous and the updated state value is not available until the next render.您在 for 循环中看不到更新的原因是setState()是异步的,并且更新的 state 值在下一次渲染之前不可用。 So you are just looping through your string, logging the current state value (which is an empty string) and then calling setState which will be called once the loop is finished.所以你只是循环你的字符串,记录当前的 state 值(这是一个空字符串),然后调用setState ,一旦循环完成就会调用它。 see: Console.log() after setState() doesn't return the updated state请参阅: setState() 后的 Console.log() 不返回更新后的 state

When thinking about a loop like this in the context of react you need to take into account the larger state/render cycle.在 react 的上下文中考虑这样的循环时,您需要考虑更大的状态/渲染周期。 The 'loop' of the animation is actually successive renders triggered by state changes. animation 的“循环”实际上是由 state 更改触发的连续渲染。

The useEffect hook gives you a built in method of leveraging this cycle, meaning that you simply need to track index through sequential renders (the snippet uses useRef to persist the value) and use it to update state, thus triggering a new render, etc. useEffect钩子为您提供了利用此循环的内置方法,这意味着您只需通过顺序渲染跟踪index (片段使用useRef来保持值)并使用它来更新 state,从而触发新的渲染等。

Here is a quick snippet.这是一个快速的片段。

 const App = () => { const [placeholder, setPlaceholder] = React.useState(''); const string = 'This is the final string.', index = React.useRef(0); React.useEffect(() => { function tick() { setPlaceholder(prev => prev + string[index.current]); index.current++; } if (index.current < string.length) { let addChar = setInterval(tick, 500); return () => clearInterval(addChar); } }, [placeholder]); return ( <div> {placeholder} </div> ) } ReactDOM.render( <App />, document.getElementById("root") );
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div>

Some other solution is not working out of the box but was kinda helpful.其他一些解决方案不是开箱即用的,但有点帮助。 Here's a more complete working solution.这是一个更完整的工作解决方案。

import React from 'react';

interface SlowTextProps {
  speed: number;
  text: string;
}

const SlowText = (props: SlowTextProps) => {
  const { speed, text } = props;
  const [placeholder, setPlaceholder] = React.useState(text[0]);

  const index = React.useRef(0);

  React.useEffect(() => {
    function tick() {
      index.current++;
      setPlaceholder((prev: string) => prev + text[index.current]);
    }
    if (index.current < text.length - 1) {
      let addChar = setInterval(tick, speed);
      return () => clearInterval(addChar);
    }
  }, [placeholder, speed, text]);
  return <span>{placeholder}</span>
}

const App = () => {
  return (
    <div>
      <SlowText speed={100} text={"Hello World 1"} />
      <SlowText speed={200} text={"Hello World 2"}/>
      <SlowText speed={500} text={"Hello World 3"} />
    </div>
  )
}

export default App;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM