Как выбрать один элемент списка при использовании карты в React для изменения значения состояния

#reactjs #use-state

Вопрос:

Я очень новичок в реагировании и создаю свое первое в истории приложение, которое представляет собой приложение для сокращения URL-адресов. Рядом с каждым сокращенным URL-адресом есть кнопка, текст которой изначально настроен на «копировать», и как только пользователь нажимает на нее, ссылка копируется в буфер обмена, а текст кнопки изменяется на «скопировано». Все работает нормально, за исключением того, что у меня есть несколько сокращенных URL-адресов, и я нажимаю на одну из кнопок рядом с каким-либо конкретным URL-адресом, он по-прежнему копирует только этот URL-адрес в буфер обмена, но текст кнопки меняется на скопированный на всех из них. Если кто-нибудь может, пожалуйста, просветить меня, как выделить эти кнопки по отдельности, это будет очень полезно. Я пробовал использовать идентификатор, но, может быть, я делаю это неправильно?

P. S — это первый раз, когда я публикую здесь, так что заранее приношу извинения, если пропустил какие-либо важные моменты.

 import {useState} from 'react'
import axios from 'axios'
import { v4 as uuidv4 } from 'uuid';

function Main() {
    const [name, setName] = useState('')
    const [list, setList] = useState(initialList);
    const handleSubmit = (e) => {
        e.preventDefault();
    }
    const handleAdd = async () => {
        const res = await axios.get(`https://api.shrtco.de/v2/shorten?url=${name}`)
        const {data: {result: {full_short_link: shortLink}}} = res 
        const newList = list.concat({name:shortLink, id: uuidv4()});
        setList(newList);
        setName('');
      }
    const [buttonText, setButtonText] = useState("Copy");

return (

    <form onSubmit={handleSubmit}>
        <input type="text" 
        value= {name} 
        onChange={(e) => setName(e.target.value)}
        placeholder='Shorten a link here'
        onClick = {()=> setButtonText('copy')}
        />
        <button onClick = {handleAdd}>Shorten it!</button>
        </form>
            
        <ul>
            {list.map((item, index) => (
            <li key={item.id}>{item.name}<button 
            onClick = {() => { navigator.clipboard.writeText(item.name); setButtonText("Copied")}} >
            {buttonText}
            </button></li>))}
        </ul>

export default Main``
 

Ответ №1:

Это связано с тем, что вы используете одну переменную состояния для всех ваших кнопок, вам нужна переменная для отслеживания состояния каждой отдельной кнопки. Вам следует преобразовать код в вашей функции карты в ее собственный компонент и объявить состояние buttonText в этом компоненте. Таким образом, каждая кнопка имеет свое собственное состояние.

Например (извините за заглавные буквы в моем коде):

MyButton.js

 Const MyButton = ({item}) => {
  const [buttonText, setButtonText] = useState(‘Copy’)

  Return (
    <li key={item.id}>{item.name}
      <button 
        onClick = {() => { 
          navigator.clipboard.writeText(item.name); 
          setButtonText("Copied")}
        } 
      >
        {buttonText}
      </button>
    </li>
)
Export default MyButton
 

Форма:

 // ……

<ul>
 {list.map((item, index) => <MyButton key={item.id} item={item} />)}
</ul>
 

Комментарии:

1. Привет, Coot3, спасибо за ваш быстрый ответ. есть ли какой-либо способ, которым это можно сделать в одном и том же компоненте, т. е. объявить состояние для каждой кнопки. или это должен быть отдельный компонент?

2. Вы, конечно, могли бы (используя объект, то есть, например, для хранения ваших значений { [id]: buttonText } ), но в React вам часто следует пытаться разбивать вещи на наименьшие возможные элементы, а затем создавать из них более сложные элементы. Посмотрите здесь (если вы еще этого не сделали): reactjs.org/docs/thinking-in-react.html