我使用的是功能组件.我使用useRef来存储previousValueCount,因为我不希望它在组件更新时重新呈现它.This works perfect as long as my 100 declaration is in the component that my code reads and writes with that ref in.然而,意识到我可能需要这个更高的树,我转向props .然而,当我将useRef声明移到父级并将ref作为props 传递时,代码似乎崩溃了,告诉我它是空的,无论初始化值是什么,而且它以前在子级中工作.据我所知,我不能通过裁判作为props ,所以我转向React.forwardRef.我try 了同样的事情,但没有解决方案,下面分享了一个类似的错误.如何将ref作为props 或组件树向下传递?

应用程序组件:

function App() {
  const [itemsLeftCount, setItemsleftCount] = useState(0);

  return (
    <ToDos
      itemsLeftCount={itemsLeftCount}
      setItemsLeftCount={setItemsLeftCount}
    ></ToDos>
  )
}

父零部件:

function ToDos(props) {
  const prevItemsLeftCountRef = useRef(0);

  return (
    <ToDoBox
      itemsLeftCount={props.itemsLeftCount}
      setItemsLeftCount={props.setItemsLeftCount}
      ref={prevItemsLeftCountRef}
    ></ToDoBox>

  {

子组件:

const ToDoBox = React.forwardRef((props, ref) => {
  useEffect(() => {
    //LINE 25 Error points to right below comment
    ref.prevItemsLeftCountRef.current = props.itemsLeftCount;
  }, [props.itemsLeftCount]);

  useEffect(() => {
    //ON MOUNT
    props.setItemsLeftCount(ref.prevItemsLeftCountRef.current + 1);

    //ON UNMOUNT
    return function removeToDoFromItemsLeft() {
      //this needs to only run after setitemsleftcount state is for sure done updating
      props.setItemsLeftCount(ref.prevItemsLeftCountRef.current - 1);
    };
  }, []);
})

我收到此错误: Uncaught TypeError: Cannot read properties of null (reading 'prevItemsLeftCountRef') at ToDoBox.js:25:1

@穆斯塔法·瓦利德

function ToDos(props) {
  const prevItemsLeftCountRef = useRef(0);
  const setPrevItemsLeftCountRef = (val) => {
    prevItemsLeftCountRef.current = val;
  };

 return (
   <ToDoBox
     itemsLeftCount={props.itemsLeftCount}
     setItemsLeftCount={props.setItemsLeftCount}
     prevItemsLeftCountRef={prevItemsLeftCountRef}
     setPrevItemsLeftCountRef={setPrevItemsLeftCountRef}
    ></ToDoBox>
  )
}

function ToDoBox(props) {
  useEffect(() => {
    //itemsLeftCount exists further up tree initialized at 0
    props.setPrevItemsLeftCountRef(props.itemsLeftCount); 
  }, [props.itemsLeftCount]);

}

推荐答案

Issue

这一切你都不需要裁判.您应该使用react 状态.我真的不能确切地确定导致双首字母递减的任何一个具体问题,但代码中有相当多的问题都值得一提.

Solution

首先,将安装件useEffect钩在♪ToDoBox♪部件中.我看到您试图保留和维护105个物品计数状态值、当前值和以前的值.这是完全多余的,并且如代码所示,可能会导致状态同步问题.简单地说,您应该在这里使用functional state update来正确地访问先前的物品计数状态值来递增或递减它.您可能还希望判断当前的♪ToDoBox♪组件不是返回的清理函数中的"创建框",以确保创建待办事项的♪ToDoBox♪不会意外地更新项目计数.

示例:

useEffect(() => {
  // RUNS ON MOUNT
  if (!isCreateBox) {
    setItemsLeftCount(count => count + 1);
  }
  
  return function removeToDoFromItemsLeft() {
    if (!isCreateBox) {
      setItemsLeftCount(count => count - 1);
    }
  };
}, []);

仅此一点就足以阻止物品数量加倍减少的问题.

Other issues I noticed

状态Mutations

添加102个删除待办事项的两个函数都在改变状态.这就是为什么你必须在待办事项中添加辅助的state状态,以迫使你的应用程序重新渲染和显示Mutations .

AddToDoToToList

这里updatedArr是对dataArr状态对象/数组的引用,该函数将102推入数组(Mutations ),然后将完全相同的对象/数组引用保存回状态.

function AddToDoToToList() {
  let updatedArr = dataArr;   // <-- saved reference to state
  updatedArr.push(dataInput); // <-- mutation!!
  setDataArr(updatedArr);     // <-- same reference back into state
}
function handleClickCreateButton() {
  AddToDoToToList(); // <-- mutates state
  //force a rerender of todos
  setState({});    // <-- force rerender to see mutation
}

处理ClickDeleteButton

这里,类似地,updateArr是对当前状态的引用.Array.prototype.splice会就地改变array.并且再次,Mutations 的状态参考被保存回状态,并且需要力重现器.

function 处理ClickDeleteButton() {
  // if splice from dataArr that renders a component todobox for each piece of data
  // if remove data from dataArr, component removed...
  let updatedArr = dataArr;    // <-- saved reference to state
  updatedArr.splice(index, 1); // <-- mutation!!
  setDataArr(updatedArr);      // <-- same reference back into state

  // then need to rerender todos... I have "decoy state" to help with that
  setState({});
}

奇怪的是,这段代码周围的注释暗示您甚至知道/理解出了问题.

这里的解决方案是again使用功能状态更新来正确地从先前状态更新101同时返回新的对象/数组引用,从而REACT看到状态已经更新并触发重现器.我还建议在待办事项上加上id个GUID,以便更容易识别它们.这比使用数组索引要好.

例如:

import { nanoid } from 'nanoid';

...

function addTodo() {
  // concat to and return a new array reference
  setDataArr(data => data.concat({
    id: nanoid(),
    todo: dataInput,
  }));
}

function removeTodo(id) {
  // filter returns a new array reference
  setDataArr(data => data.filter(todo => todo.id !== id));
}

其他一般设计问题

  • Props drilling of the root itemsLeftCount state and setItemsLeftCount state updater function all the way down to the leaf ♪ToDoBox♪ components.
  • 没有集中控制dataArr个状态不变量.
  • 使用映射的数组索引作为react 键.
  • 正在try 计算嵌套子对象中的"派生"状态,即项目计数.

以下是您的代码的精简/精简工作版本:

APP

function APP() {
  const [itemsLeftCount, setItemsLeftCount] = useState(0);

  return (
    <div>
      <待办事项 setItemsLeftCount={setItemsLeftCount} />
      <div>{itemsLeftCount} items left</div>
    </div>
  );
}

待办事项

import { nanoid } from "nanoid";

function 待办事项({ setItemsLeftCount }) {
  const [dataInput, setInputData] = useState("");
  const [dataArr, setDataArr] = useState([]);

  useEffect(() => {
    // Todos updated, update item count in parent
    setItemsLeftCount(dataArr.length);
  }, [dataArr, setItemsLeftCount]);

  function getInputData(event) {
    setInputData(event.target.value);
  }

  function addTodo() {
    setDataArr((data) =>
      data.concat({
        id: nanoid(),
        todo: dataInput
      })
    );
  }

  function removeTodo(id) {
    setDataArr((data) => data.filter((todo) => todo.id !== id));
  }

  return (
    <div>
      <♪ToDoBox♪ isCreateBox getInputData={getInputData} addTodo={addTodo} />
      {dataArr.map((data) => (
        <♪ToDoBox♪ key={data.id} data={data} removeTodo={removeTodo} />
      ))}
    </div>
  );
}

♪ToDoBox♪

function ♪ToDoBox♪({ addTodo, data, getInputData, isCreateBox, removeTodo }) {
  if (isCreateBox) {
    return (
      <div>
        <input
          placeholder="Create a new todo..."
          onChange={getInputData}
          tabIndex={-1}
        />
        <span onClick={addTodo}>+</span>
      </div>
    );
  } else {
    return (
      <div>
        <span>{data.todo}</span>{" "}
        <span onClick={() => removeTodo(data.id)}>X</span>
      </div>
    );
  }
}

Edit react-cannot-read-properties-of-null-of-ref-with-forwardref-and-useref

Javascript相关问答推荐

如何循环访问对象数组并以关键值形式获得结果?

Vega中的模运算符

通过使用100%间隔时间来代表我们还剩多少时间来倒计时

react/redux中的formData在expressjs中返回未定义的req.params.id

调用removeEvents不起作用

扫描qr code后出错whatter—web.js

我的服务工作器没有连接到我的Chrome扩展中的内容脚本.我该怎么解决这个问题?

如何从隐藏/显示中删除其中一个点击?

您能在卸载程序(QtInsteller框架)上添加WizardPage吗?

禁用.js文件扩展名并从目录导入隐式根index.js时,找不到NodeJS导入模块

有条件重定向到移动子域

如何在FastAPI中为通过file:/URL加载的本地HTML文件启用CORS?

按什么顺序接收`storage`事件?

我正在试着做一个TicTacToe Ai来和我玩.但是,我试着在第一个方块被点击时出现一个X,然后在第二个方块之后出现一个O

如果对象中的字段等于某个值,则从数组列表中删除对象

react 路由DOM有条件地呈现元素

在Java脚本中构建接口的对象

表单数据中未定义的数组键

rxjs在每次迭代后更新数组的可观察值

JSON Web令牌(JWT)错误:RSA密钥对的签名无效