如何在 React Hooks 中使用 splice

问题描述

当我在选择框中选择一个 ID 时,我想在名称数组的选定 ID 中放置一个名称。我使用了 useState 和 useRef,但“拼接”不起作用。我不知道如何将选定的 ID 传递给拼接功能。这对我来说太难了。请帮帮我。

import React,{ useRef,useState } from 'react';
const App = ()=>{
  const [names,setNames] = useState([
    {id: 1,text: 'aaa'},{id: 2,text: '222'},{id: 3,text: 'bbb'},]);
  const [inputText,setInputText] = useState('');
  const [nextId,setNextId] = useState(4);
  const onChange = e => setInputText(e.target.value);
  const inputEl = useRef(null);
  const optionId = useRef(null);
  const onClick = () => {
      const nextNames = names.concat({
        id: nextId,text: inputText,});
      setNextId(nextId + 1);
      setNames(nextNames);
      setInputText('');
      console.log(inputEl.current);
      console.log(optionId.current);
  };
  const onSelect = id => {
    const nextNames = names.splice(id,{id:id,text: inputText});
  
    setNames(nextNames);
    setInputText('');
  }
  const onRemove = id => {
    const nextNames = names.filter(name => name.id !== id);
    setNames(nextNames);
  };
  const nameList = names.map(name => (
    <li key={name.id} ondoubleclick={()=> onRemove(name.id)}>{name.text}</li>
  ));
  const idOption = names.map((name,index)=>(
     <option key={name.id}>{index}</option>
     ));
  return(
    <div>
      <input value={inputText} onChange={onChange} ref={inputEl}/>
      <button onClick={onClick}>추가</button>
      <select ref={optionId} onSelect={onSelect}>
        <option>ID</option>
            {idOption}
        <option>last</option>
      </select>
      <ul>{nameList}</ul>
    </div>
  );
};

解决方法

Array.prototype.splice 改变它在就地调用的数组,这几乎总是 > 当数组是状态的一部分时,想要使用并且在 React 中被认为是反模式。需要对数组进行浅拷贝,才能正确返回新的数组引用,用于 React 的对帐过程。

splice() 方法通过删除或更改数组的内容 替换现有元素和/或添加新元素就地

由于您要向数组添加新元素更新现有元素,您首先需要搜索数组以确定需要执行的操作。如果添加新元素,您可以简单地将前一个数组浅复制到新的数组引用中并附加新元素。如果更新现有元素,那么将之前的状态映射到新数组就是您想要的。

const onSelect = id => {
  setNames(names => {
    // find if match exists
    const match = names.find(name => name.id === id);

    if (match) {
      // match found,map array
      return names.map(name => name.id === id
        ? {
          ...name,text: inputText,}
        : name
      );
    }
    
    // no match found,concat new data
    return names.concat({ id,text: inputText });
  });
  setInputText('');
}
,

Splice 接受索引,因此检查 reference 将不起作用。

但是您应该使用 slice 代替,因为 splice 会改变您正在处理的数组,并且您不应该直接改变 useState 值。有关详细信息,请参阅切片 reference

尽管如此,您有更好的选择来完成您想做的事情,例如 mapreduce

这是一个使用 reduce 的例子:

  const onSelect = id => {
    const nextNames = names.reduce((acc,next) => {
      const name = {...next};

      // only change the text prop if we find an id match
      if (name.id === id) {
        name.text = inputText;
      }

      // copy the name to the new array
      return [...acc,name];
    },[]);
  
    setNames(nextNames);
    setInputText('');
  }
,

感谢您的回答。但是,它不能正常工作。下面代码的工作正是我想要的。这些代码是类组件样式,但我需要在 React Hooks 中进行相同的工作。请看下面的代码,如果可能,请更改为带有反应钩子的代码。谢谢。

class App extends React.Component {
  constructor() {
    super();
    this.state = {
      components: []
    };
  }

  addNewElement(element,selectedIndex) {
    if (selectedIndex == "last") {
      this.state.components.push(element);
    } else {
      this.state.components.splice(selectedIndex,element);
    }
    this.setState({ components: this.state.components });
  }
  render() {
    let input,option;
    // log out state
    console.log(this.state.components);
    return (
      <div>
        <h3>{this.state.components.join(",")}</h3>
        <input
          placeholder="enter element"
          ref={node => {
            input = node;
          }}
          />
        <select
          className={this.state.components.length ? "" : "hidden"}
          ref={node => {
            option = node;
          }}
          >
          <option value="" disabled selected>Insert at index...</option>
          {this.state.components.map((component,index) =>
                                     <option>{index}</option>
                                    )}
          <option>last</option>
        </select>
        <button
          onClick={() => {
            this.addNewElement(input.value,option.value);
          }}
          >
          Click
        </button>
      </div>
    );
  }
}

ReactDOM.render(<App />,document.getElementById("root"));