React 列表不会无缘无故更新(据我所知)

React list is not updated for no reason (as far as I know)

提问人:Epikdino 提问时间:10/13/2023 更新时间:10/13/2023 访问量:68

问:

我完成了 reactjs 教程并尝试制作我的第一页。 我制作了一个按钮来生成盒子,并在每个盒子中制作按钮来移动或删除它们。 我制作了一个移动部件不起作用的版本,我仍然不明白为什么。然后,我向 bard 询问了一个更简单的版本,然后对其进行了扩展以满足我的要求。你能帮我理解为什么第一个版本不起作用吗?基本上,图表(charts)的列表永远不会改变。图表的索引始终为 -1,但我的类型对我来说看起来不错...... 这是不起作用的代码:

import { useState } from 'react';
import './App.css';

function TopSelect({ gpios, addChart }){
  const options = []

  function handleSubmit(e){
    e.preventDefault();
    const data = new FormData(e.target);
    const formJson = Object.fromEntries(data.entries());
    addChart(formJson.gpios);
  }
  
  gpios.forEach(e => {
    options.push(<option key={e} value={e}>GPIO{e}</option>);
  })

  return <form onSubmit={handleSubmit}>
    <label>
      GPIO
    </label>
    <select name="gpios">
        {options}
    </select>
    <button type="submit">Add chart</button>
  </form>;
}

function Chart({ gpio, pos, removeChart, moveChart}){
  return <li key={pos}>
    <h3>GPIO{gpio}</h3>
    <button onClick={()=>moveChart(pos, false)}>Up</button>
    <button onClick={()=>moveChart(pos, true)}>Down</button>
    <button onClick={()=>removeChart(pos)}>Remove</button>
  </li>;
}

function App() {
  const [ gpios, setGpios ] = useState([]);
  const [ charts, setCharts ] = useState([]);
  const [ gpioInitialized, setGpioInitialized ] = useState(false);

  function initGpio(){
    let newGpio = [];
    [1, 4, 7, 12].forEach(x => (
      newGpio.push(x)
    ));
    setGpios(newGpio);
  }

  function addChart(gpio){
    let pos = 0;
    if(charts.length > 0) pos = Math.max(...charts.map(c => c.id)) + 1;
    const newChart = {id: pos , 
      elem : <Chart gpio={gpio} pos={pos} removeChart={removeChart} moveChart={moveChart}/>};
    setCharts([...charts, newChart ]);
  }

  function removeChart(pos){
    setCharts((current) => current.filter((chart) => chart.id !== pos));
  }

  function moveChart(pos, down){
    const index = charts.findIndex((chart) => chart.id === pos);
    console.log(index);
    if(!down){
      if (index <= 0) return;
      setCharts((charts) => {
        const chartsCopy = [...charts];
        const [chart] = chartsCopy.splice(index, 1);
        chartsCopy.splice(index - 1, 0, chart);
      });
    }else{
      if (index >= charts.length - 1) return;
      setCharts((charts) => {
        const chartsCopy = charts.slice();
        const [chart] = chartsCopy.splice(index + 1, 1);
        chartsCopy.splice(index, 0, chart);
      });
    }
  }

  if(!gpioInitialized){
    initGpio();
    setGpioInitialized(true);
  }

  return (
    <div className="App">
      <TopSelect gpios={gpios} addChart={addChart} />
      <ul>
        {
          charts.map((chart) =>
            chart.elem
          )
        }
      </ul>
    </div>
  );
}

export default App;

这是工作版本:

import React, { useState } from "react";

function Menu({ gpios, addBox }){
  const options = []

  function handleSubmit(e){
    e.preventDefault();
    const data = new FormData(e.target);
    const formJson = Object.fromEntries(data.entries());
    addBox(formJson.gpios);
  }
  
  gpios.forEach(e => {
    options.push(<option key={e} value={e}>GPIO{e}</option>);
  })

  return <form onSubmit={handleSubmit}>
    <label>
      GPIO
    </label>
    <select name="gpios">
        {options}
    </select>
    <button type="submit">Add chart</button>
  </form>;
}

function Box({ id, gpio, removeBox, moveBoxUp, moveBoxDown}){
  const boxStyle = {
    width: "100%",
    height: "100px",
    margin: "10px 0",
    padding: "10px",
    border: "1px solid black",
    backgroundColor: "white",
  };
  let buttonUp = "";
  let buttonDown = "";
  
  if(moveBoxDown){
    buttonDown = <button
          onClick={() => moveBoxDown(id)}
        >
          Move Down
        </button>;
  }

  if(moveBoxUp){
    buttonUp = <button
        onClick={() => moveBoxUp(id)}
          >
        Move Up
      </button>;
  }
  return <li key={id}>
      <div style={boxStyle}>
        <h1>GPIO {gpio}</h1>
        <button
          onClick={() => removeBox(id)}
        >
          Remove
        </button>
        {buttonUp}
        {buttonDown}
      </div>
    </li>;
}

function App(){
  const [gpios, setGpios] = useState([]);
  const [gpioInitialized, setGpioInitialized] = useState(false);
  const [boxes, setBoxes] = useState([]);

  function initGpios(){
    if(gpioInitialized) return;
    const available = [1,4,7,13];
    setGpios(available);
    setGpioInitialized(true);
  }

  function addBox(gpio){
    const newBox = {
      id: Date.now(),
      gpio: gpio,
    };

    setBoxes([...boxes, newBox]);
  };

  function removeBox(id){
    setBoxes(boxes.filter((box) => box.id !== id));
  }

  function moveBoxUp(id){
    const index = boxes.findIndex((box) => box.id === id);

    if (index > 0) {
      setBoxes((boxes) => {
        const boxesCopy = boxes.slice();
        const [box] = boxesCopy.splice(index, 1);
        boxesCopy.splice(index - 1, 0, box);
        return boxesCopy;
      });
    }
  }

  function moveBoxDown(id){
    const index = boxes.findIndex((box) => box.id === id);

    if (index < boxes.length - 1) {
      setBoxes((boxes) => {
        const boxesCopy = boxes.slice();
        const [box] = boxesCopy.splice(index + 1, 1);
        boxesCopy.splice(index, 0, box);
        return boxesCopy;
      });
    }
  };

  initGpios();

  return (
    <div>
      <Menu gpios={gpios} addBox={addBox} />

      <ul>
        {boxes.map((box, index) => (
          <Box id={box.id} gpio={box.gpio} removeBox={removeBox} moveBoxDown={(index===boxes.length-1)?null:moveBoxDown} moveBoxUp={(index===0)?null:moveBoxUp}/>
        ))}
      </ul>
    </div>
  );
}

export default App;

有了新版本,盒子可以完美地移动。 谢谢! PS:如果您有任何提示或有些线条灼伤您的眼睛,请告诉我!

JavaScript ReactJS 列表 react-hooks

评论


答:

0赞 Helper 10/13/2023 #1

我必须说,我已经浏览了您的代码,它看起来结构良好,对于初学者来说很不错。

我有几点可以考虑,以进一步改进你的代码:

  1. 开始使用 (https://www.freecodecamp.org/news/how-to-use-proptypes-in-react/) 或打字稿(这可能需要很多时间来学习),但我认为现在您可以继续。这有助于非常容易地捕获错误。prop-typesprop-types

  2. 我可以看到你已经使用了 ,例如,尝试使用唯一标识符。indexkeyid

  3. 我认为应该避免内联样式,这样你的代码会更干净。

  4. 我相信您的代码可以进一步分解为多个新组件。

  5. 请尝试使用一致的变量名称。我可以看到你有和两者。boxesbox

不过,作为学习者的良好开端!我鼓励你学习和成长更多。

-1赞 Exter 10/13/2023 #2
  • <button onClick={()=>moveChart(pos, false)}>Up</button>,这未按预期工作。正在发生的事情是一个函数以某种方式被注册(同时声明此 onclick),并且该函数使用的所有值 - 包括 ,也被注册。 因此,在添加 5 个图表后,您将其中一个图表向上移动,该函数将有一个长度为 1 的数组,而不是预期的 5,因此事情没有按预期工作。为什么会发生这种情况对我来说也是新事物,如果您发现它,请告诉我。moveChartchartsmoveChartcharts

您可以通过声明新状态来测试这一点。添加一次所有图表,然后尝试移动它们。理想情况下,您应该获得相同的输出,但您获得的值是声明 和 本身时的值。valchartonClick

const [val, setVal] = useState(-5);

递增函数中的状态,addChart

setVal((val) => val + 5);
setCharts([...charts, newChart]);

并在函数内部打印它 -moveChart

function moveChart(pos, down) {
    console.log(pos, charts, val);

  • 在声明 时已经定义了 for 上发生的事情,因此即使位置发生变化,函数也会调用将 old 作为其参数。onClickmovechartmovepos
  • 移动箱子时,您用来确定其位置的箱子不会改变。因此,它们现在被映射到错误的位置。id
  • 此外,请尝试向所有代码添加注释。
  • chart我猜在任何地方都没有使用,const [chart] = chartsCopy.splice(index + 1, 1);

这些是我能找到的一些东西,也许还有更多。

评论

0赞 Epikdino 10/16/2023
谢谢!我想我很难理解什么被调用/呈现以及何时呈现。