reactjs 使用UseState钩子从React中的对象内的数组中删除元素

eaf3rand  于 2023-06-22  发布在  React
关注(0)|答案(3)|浏览(178)

我试图使用UseState钩子从React中的对象数组中删除一个元素,但很难得到结果,因为接口不会重新渲染,对象也不会从中删除。
据我所知,要在react UI中反映对象的任何更新,我应该替换整个对象而不是更新它。我试图创建一个数组的副本,并通过函数内部的setDives方法分配它,但什么也没有。
下面是我的代码:
使用useState声明const。我只需要按名称删除特定的指南(本例中为“Lee”或“John”):

const [dives, setDives] = useState([
    { boat: 'Marcelo', divesite: '', guides: ['Lee', 'Jhon'] },
]);

删除功能:

function deleteGuide(i, guide) {
    var tempArray = dives;
    tempArray[dives.indexOf(i)].guides = dives[dives.indexOf(i)].guides.filter(
        (e) => e !== guide,
    );
        
    setDives(tempArray);
}

界面按钮:

{dives.map((i) => (
        <div>
            {dives[dives.indexOf(i)].guides.map((guide) => (
                <ButtonGroup>
                    <Button>{guide}</Button>
                    <Button onClick={() => deleteGuide(i, guide)}></Button>
                </ButtonGroup>
            ))}
        </div>
    ))}
ozxc1zmp

ozxc1zmp1#

你说得对,你必须创建一个新的数组副本,但你实际上没有这样做。这就是为什么它不工作。
更改此行:setDives(tempArray);为:setDives([...tempArray]);
说明:当只设置tempArray时,您只更新引用,它不会更改,因为tempArray = dives。由于没有更改,因此不会重新渲染,也不会看到任何结果。通过使用扩展操作符...,构造了一个全新的Array,因此状态发生了变化(这会触发重新渲染)。
进一步阅读:https://react.dev/learn/updating-arrays-in-state
为了更容易理解,你可以在setDives(tempArray)-语句之前添加一个console.log,然后看看输出:

console.log(tempArray === dives); //will always return true, so no change
setDives([...tempArray]);
e0bqpujr

e0bqpujr2#

有几样东西我会推荐。
1.为潜水和向导添加id到数据集(这意味着将向导从名称字符串更改为带有idname的对象。它有助于减轻当你在map ping数组并试图添加键时React会给予你的一些警告。
1.在button元素上使用这些id作为data attributes。您可以在按钮处理程序中选择它们,并使用它们来帮助您筛选数据集。

const { Fragment, useState } = React;

// For the purposes of this example I'm passing in the data
// as `config` and initialising it as state
function Example({ config }) {

  const [dives, setDives] = useState(config);
    
  // Your delete button handler
  function handleDeleteGuide(e) {

    // Destructure the diveid and the guideid from the
    // button's dataset
    const { diveid, guideid } = e.target.dataset;

    // `map` over the dataset
    // If the current dive object's id matches the button's
    // diveid value return a new object that spreads out the
    // current object, and updates the guide array by filtering
    // out the guide whose id doesn't match the button's guideid value.
    // Otherwise, if there is no match, return the dive object
    const updated = dives.map(dive => {
      if (Number(diveid) === dive.id) {
        return {
          ...dive,
          guides: dive.guides.filter(guide => {
            return Number(guideid) !== guide.id;
          })
        };
      }
      return dive;
    });

    // Set the new state with the array that our
    // `map` operation provided
    setDives(updated);
  }

  // Here I'm iterating over the dive objects
  // and then the guide objects making sure to add
  // the appropriate keys.
  // You can see on the button that there are two data
  // attributes (diveid/guideid) which contain, unsuprisingly,
  // the dive id and the guide id - these are what's picked up
  // in the buttons click handler
  // You may want to split these up into separate components
  // at some point before it gets too messy
  return (
    <section>
      {dives.map(dive => {
        const { id, boat, guides } = dive;
        return (
          <Fragment key={id}>
            <h4>Boat: {boat}</h4>
            <h4>Guides</h4>
            <ul>
              {guides.map(guide => {
                const { id: guideId, name } = guide;
                return (
                  <li key={guideId}>
                    {name}
                    <button 
                      className="delete"
                      type="button"
                      data-diveid={id}
                      data-guideid={guideId}
                      onClick={handleDeleteGuide}
                    >Delete
                    </button>
                  </li>
                );
              })}
            </ul>
          </Fragment>
        );
      })}
    </section>
  );

}

const config = [
    { id: 1, boat: 'Marcelo', divesite: '', guides: [{ id: 1, name: 'Lee' }, { id: 2, name: 'Jhon'}] },
    { id: 2, boat: 'Calico', divesite: '', guides: [ { id: 1, name: 'Bruce'}, {id: 2, name: 'Johanna' }] }
];
const node = document.getElementById('root');
const root = ReactDOM.createRoot(node);
root.render(<Example config={config} />);
ul { list-style: none; margin: 0; padding: 0;}
li:not(:first-child) { margin-top: 0.25rem; }
.delete { margin-left: 0.25rem; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.min.js"></script>
<div id="root"></div>
bprjcwpo

bprjcwpo3#

调用setDives时,应使用setState的函数版本。您可以使用structuredClone创建当前状态的深层副本。

**注意:**在调用map时,还需要使用key prop。

const { useCallback, useState } = React;

const uuidv4 = () =>
  ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
    (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16));

const App = () => {
  const [dives, setDives] = useState([
    { boat: 'Marcelo', divesite: '', guides: ['Lee', 'Jhon'] },
  ]);
  
  const deleteGuide = useCallback((e, i) => {
    const guideToRemove = e.target.closest('.ButtonGroup').dataset.guide;
    console.log(`Removing ${guideToRemove}...`);
    setDives((currentDives) => {
      const copyDives = structuredClone(currentDives);
      copyDives[i].guides = copyDives[i].guides.filter((guide) => guide !== guideToRemove);
      return copyDives;
    });
  }, []);

  return (
    <div>
      {dives.map((dive, i) => (
        <div key={uuidv4()} className="DiveInfo">
          {dive.guides.map((guide) => (
            <div key={guide} className="ButtonGroup" data-guide={guide}>
              <button type="button">{guide}</button>
              <button type="button" className="DeleteButton"
                onClick={(e) => deleteGuide(e, i)}></button>
            </div>
          ))}
        </div>
      ))}
    </div>
  );
};

ReactDOM
  .createRoot(document.getElementById('root'))
  .render(<App />);
html, body, #root, #root > div {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

#root > div {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.DiveInfo {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 0.25rem;
}

.ButtonGroup {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  min-width: 6rem;
}

.DeleteButton:after {
  content: '\00D7';
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>

相关问题