繁体   English   中英

如何使用 React Context 更新列表中的单个项目?

[英]How do I update a single item in list with React Context?

我真的很喜欢 React 上下文,但我认为它缺少一些东西(或者我不知道该怎么做)

假设我有一个待办事项列表,它是相应的提供者

const Home = () => (
  <div className="container">
    <TodosProvider>
      <TodosList />
    </TodosProvider>
  </div>
)

const TodosList = () => {
  const { todos } = useTodos();

  return (
    <>
      {todos.map((todo, idx) => (
          <SingleTodo />
      ))}
    </>
  )
}

在另一个文件中


import { createContext, useContext, useState } from "react";

const TodosContext = createContext({});


export const TodosProvider = ({ children }) => {

    const [todos, setTodos] = useState([{ text: 'a' }, { text: 'b' }, { text: 'c' }])

    return (
        <TodosContext.Provider value={{ todos }}>
            {children}
        </TodosContext.Provider>
    )

}


export const useTodos = () => {
    const todos = useContext(TodosContext)
    return todos
}

我如何在 SingleTodo 中更新单个待办事项而无需:

1) 将 map idx 作为属性传递给 SingleTodo,然后从 SingleTodo 调用 TodosList 提供程序的方法,将 idx 作为参数传递

2) 给 todo 一个人工的 id 属性。 然后在 TodosProvider 中更新与该 id 匹配的待办事项。

这些限制的原因是:

1) 将渲染中todo的position作为prop传递下去,使使用context的好处失效,即不必做prop drilling

2)我认为仅仅为了 state 管理而用人工 id 污染 model 是不好的。

我希望能够在循环的每次迭代中创建一个 SingleTodoContext 并实例化一个 SingleTodoProvider

const TodosList = () => {
  const { todos } = useTodos();

  return (
    <>
      {todos.map((todo, idx) => (
        <SingleTodoProvider key={idx} loadFrom={todo}>
          <SingleTodo />
        </SingleTodoProvider>
      ))}
    </>
  )
}

但这不起作用,因为提供者随后需要将 loadFrom 属性存储为 state,这会破坏列表待办事项和单个待办事项之间的同步。

那么,如何在不钻取列表中项目的 position 的情况下更新列表中的单个项目? 我不想使用 Redux

您可以将用于更新上下文中的值的方法作为上下文的一部分传递。 这是一个基于您的代码的 示例(有点挤在一起):

import React from "react";
import "./styles.css";
import { createContext, useContext, useState } from "react";

const TodosContext = createContext({});

export const TodosProvider = ({ children }) => {
  const [todos, setTodos] = useState([
    { text: "a" },
    { text: "b" },
    { text: "c" }
  ]);

  const selectTodo = (todo, idx) => {
    console.log(
      "do something with the todo here and then call setTodos, or something else?",
      todo.text,
      idx
    );

    // setTodos(prev => /* Do something here to update the list */)
  };

  return (
    <TodosContext.Provider value={{ selectTodo, todos }}>
      {children}
    </TodosContext.Provider>
  );
};

export const useTodos = () => {
  const todos = useContext(TodosContext);
  return todos;
};

const Home = () => (
  <div className="container">
    <TodosProvider>
      <TodosList />
    </TodosProvider>
  </div>
);

const SingleTodo = ({ todo, onClick }) => (
  <div>
    {todo.text} <button onClick={() => onClick(todo)}>Click Me!</button>
  </div>
);

const TodosList = () => {
  const { selectTodo, todos } = useTodos();

  return todos.map((todo, idx) => (
    <SingleTodo onClick={todo => selectTodo(todo, idx)} todo={todo} key={idx} />
  ));
};

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <Home />
    </div>
  );
}

希望有帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM