为了账号安全,请及时绑定邮箱和手机立即绑定

ReactJS 如何使用状态钩子获取超时的文本更改按钮列表?

ReactJS 如何使用状态钩子获取超时的文本更改按钮列表?

守着一只汪 2022-10-21 15:37:41
这可能会令人困惑,但我感谢您的帮助。我基本上有一个数据列表,每个数据旁边都有一个“复制”按钮。单击时,我希望它更改为“已复制!” 500 毫秒,然后返回“复制”。我的设置如下:我有一个数组,其中包含我作为道具传递给组件的数据列表(比如说数组 x)。使用这个数组,我创建了一个 useEffect 钩子(当数据数组 x 更新时更新)。这个钩子使用原始数组长度的新数组(比如说数组 y)和文本“复制”更新一个新状态。据我所知,似乎工作得很好,现在我的问题出在哪里:然后我为创建新数组的按钮创建一个处理程序,使用点符号展开并将按钮的索引从“复制”更新为“抄袭!”。然后超时将其恢复为“复制”(下面的代码)。效果很好,但你怀疑我有一个错误。如果我在另一个按钮上太快地点击复制,我最终会将原来的“复制”按钮卡在那里。正如我猜测的那样,我更新状态的速度更快,然后另一个状态回落。function copyButtonClipBrd(index) {  console.log("Test Copy Button");  console.log(cpyBTN);  let newArr = [...cpyBTN];  newArr[index] = "Copied!";  setCpyBTN(newArr);    setTimeout(function () {    let newArr = [...cpyBTN];    newArr[index] = "Copy";    setCpyBTN(newArr);  }, 600);}会喜欢任何建议,甚至更好的方法来做到这一点,因为我这样做主要是为了学习。谢谢大家!
查看完整描述

4 回答

?
慕桂英546537

TA贡献1848条经验 获得超10个赞

您不需要更新状态,当单击任何按钮时,只需在按钮上显示不同的文本。

在包含所有数据的列表的父元素上添加单击事件侦听器,并检查触发click事件的元素是否为复制按钮。如果是,请更改其文本并设置一个计时器,将文本恢复为“复制”

演示:

const url = 'https://jsonplaceholder.typicode.com/todos';


function App() {

  const [todos, setTodos] = React.useState([]);

  

  React.useEffect(() => {

    fetch(url)

      .then(res => res.json())

      .then(todos => setTodos(todos))

      .catch(err => console.log(err.message));

  }, []);


  return (

    <div className="App">

      <TodoList todos={todos}/>

    </div>

  );

}


function TodoList(props) {


  function changeBtnText(event) {

    const target = event.target;


    if (target.matches("button")) {

      target.textContent = "Copied";

      setTimeout(() => (target.textContent = "Copy"), 1000);

    }

  }


  return (

    <div className="todoList" onClick={changeBtnText}>

      {props.todos.map(t => (

        <Todo key={t.id} {...t} />

      ))}

    </div>

  );

}


function Todo({ title, completed }) {

  return (

    <div className="todo">

      <span>TItle: {title}</span>

      <button>Copy</button>

    </div>

  );

}


const rootElement = document.getElementById("root");

ReactDOM.render(<App />, rootElement);

.App {

  font-family: sans-serif;

  text-align: center;

}


.todo {

  box-shadow: 0 0 2px rgba(0, 0, 0, 0.3);

  margin: 10px;

  display: flex;

  flex-direction: column;

  justify-content: center;

  align-items: center;

  padding: 10px;

}


.todo button {

  padding: 5px 10px;

  margin: 10px 0 0 0;

  cursor: pointer;

}

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>


<div id="root"></div>

您还可以在 Codesandbox 上查看此演示



查看完整回答
反对 回复 2022-10-21
?
Cats萌萌

TA贡献1805条经验 获得超9个赞

import React, { useEffect, useState, useCallback, useRef } from 'react'

import { range } from 'lodash-es'


const App = () => {

  const [items, setItems] = useState(

    range(100).map((val) => ({ id: val, val, text: 'Copy' })),

  )


  const [copiedItem, setCopiedItem] = useState<number | undefined>(undefined)


  const timeout = useRef<number | undefined>(undefined)


  useEffect(() => {

    if (copiedItem) {

      setItems((itemsState) =>

        itemsState.map((item) =>

          item.id === copiedItem

            ? { ...item, text: 'Copied' }

            : { ...item, text: 'Copy' },

        ),

      )

    } else {

      setItems((itemsState) =>

        itemsState.map((item) => ({ ...item, text: 'Copy' })),

      )

    }

  }, [copiedItem])


  const handleClick = useCallback(

    (itemId) => () => {

      if (timeout.current) {

        window.clearTimeout(timeout.current)

      }

      setCopiedItem(itemId)

      timeout.current = window.setTimeout(() => {

        setCopiedItem(undefined)

      }, 1000)

    },

    [],

  )


  return (

    <div>

      {items.map((item) => (

        <div key={item.id} className="mb-4">

          <span className="mr-2">{item.val}</span>

          <button className="p-2 bg-gray-100" onClick={handleClick(item.id)}>

            {item.text}

          </button>

        </div>

      ))}

    </div>

  )

}


export default App


您可以使用另一种状态copiedItem来存储要复制的项目的 id 或索引。在 中useEffect我们可以寻找更改copiedItem以设置要复制的文本。


除此之外,我们还需要对上一个超时的引用,以便我们可以在触发下一个项目超时之前清除它。


查看完整回答
反对 回复 2022-10-21
?
慕码人8056858

TA贡献1803条经验 获得超6个赞

所以我尝试使用@Agney 建议在 setState 中使用回调。这是codesanbox链接https://codesandbox.io/s/quirky-dubinsky-lm03x?file=/src/App.js

所以基本上我们在 setstate 回调中检查索引以最初将其设置为复制,在超时回调中我们再次检查相同的索引以设置回复制。


查看完整回答
反对 回复 2022-10-21
?
收到一只叮咚

TA贡献1821条经验 获得超4个赞

这是直接使用状态对其进行操作的问题,相反,ReactJS 提供了一个回调版本setState,允许您currentState在执行时作为参数访问 。您可以将其用作:


setCpyBtn((currentState) => {

  return currentState.map((item, i) => {

    if (i === index) {

      return 'Copy';

    }

    return 'Copied';

  });

})

当然反之亦然。


文档


查看完整回答
反对 回复 2022-10-21
  • 4 回答
  • 0 关注
  • 116 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号