Javascript 如何使用React上下文更新列表中的单个项目?

Javascript 如何使用React上下文更新列表中的单个项目?,javascript,reactjs,react-native,Javascript,Reactjs,React Native,我真的很喜欢react上下文,但我认为它缺少了一些东西(或者我不知道如何做) 假设我有一个TODO列表,它对应的提供者为 const Home=()=>( ) const todolist=()=>{ const{todos}=useTodos(); 返回( {todos.map((todo,idx)=>( ))} ) } 在另一个文件中 import { createContext, useContext, useState } from "react"; const TodosCont

我真的很喜欢react上下文,但我认为它缺少了一些东西(或者我不知道如何做)

假设我有一个TODO列表,它对应的提供者为

const Home=()=>(
)
const todolist=()=>{
const{todos}=useTodos();
返回(
{todos.map((todo,idx)=>(
))}
)
}
在另一个文件中


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
}

从“react”导入{createContext,useContext,useState};
const TodosContext=createContext({});
export const todoProvider=({children})=>{
const[todos,setTodos]=useState([{text:'a'},{text:'b'},{text:'c'}])
返回(
{儿童}
)
}
导出常量useTodos=()=>{
const todos=useContext(todoContext)
返回待办事项
}
如何在没有以下内容的情况下更新SingleTodo中的单个todo:

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

2) 将人工id属性赋予todo。然后在todoProvider中更新与该id匹配的todo

这些限制的原因是:

1) 将todo在渲染中的位置作为道具传递,会使使用上下文的好处无效,即不必进行道具钻取

2) 我认为仅仅为了国家管理而用人工id污染模型是不好的

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

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

  return (
    <>
      {todos.map((todo, idx) => (
        <SingleTodoProvider key={idx} loadFrom={todo}>
          <SingleTodo />
        </SingleTodoProvider>
      ))}
    </>
  )
}
const todolist=()=>{
const{todos}=useTodos();
返回(
{todos.map((todo,idx)=>(
))}
)
}
但这不起作用,因为提供程序随后需要将loadFrom属性存储为状态,这将中断列表todo和单个todo之间的同步


那么,如何更新列表中的单个项目,而不必钻取该项目在列表中的位置?我不想使用Redux,您可以在上下文中传递更新值的方法作为上下文的一部分。下面是一个基于您的代码的示例(有点挤在一起):

从“React”导入React;
导入“/styles.css”;
从“react”导入{createContext,useContext,useState};
const TodosContext=createContext({});
export const todoProvider=({children})=>{
const[todos,setTodos]=useState([
{案文:“a”},
{案文:“b”},
{文本:“c”}
]);
常量selectTodo=(todo,idx)=>{
console.log(
“在这里用todo做些什么,然后调用setTodos,或者其他什么?”,
todo.text,
idx
);
//setTodos(prev=>/*在此处执行某些操作以更新列表*/)
};
返回(
{儿童}
);
};
导出常量useTodos=()=>{
const todos=useContext(todoContext);
返回待办事项;
};
常量Home=()=>(
);
常量SingleTodo=({todo,onClick})=>(
{todo.text}onClick(todo)}>单击我!
);
const todolist=()=>{
const{selectTodo,todos}=useTodos();
返回todos.map((todo,idx)=>(
选择todo(todo,idx)}todo={todo}键={idx}/>
));
};
导出默认函数App(){
返回(
你好,代码沙盒
开始编辑,看看神奇的发生!
);
}

希望有帮助

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

从“React”导入React;
导入“/styles.css”;
从“react”导入{createContext,useContext,useState};
const TodosContext=createContext({});
export const todoProvider=({children})=>{
const[todos,setTodos]=useState([
{案文:“a”},
{案文:“b”},
{文本:“c”}
]);
常量selectTodo=(todo,idx)=>{
console.log(
“在这里用todo做些什么,然后调用setTodos,或者其他什么?”,
todo.text,
idx
);
//setTodos(prev=>/*在此处执行某些操作以更新列表*/)
};
返回(
{儿童}
);
};
导出常量useTodos=()=>{
const todos=useContext(todoContext);
返回待办事项;
};
常量Home=()=>(
);
常量SingleTodo=({todo,onClick})=>(
{todo.text}onClick(todo)}>单击我!
);
const todolist=()=>{
const{selectTodo,todos}=useTodos();
返回todos.map((todo,idx)=>(
选择todo(todo,idx)}todo={todo}键={idx}/>
));
};
导出默认函数App(){
返回(
你好,代码沙盒
开始编辑,看看神奇的发生!
);
}

希望有帮助

嘿,谢谢你的回答!但是,如果onClick方法不在SingleTodo标记上(假设呈现todo非常困难),而是在嵌套组件中,那么如何在不钻取idx的情况下调用selectTodo?如果不想,不需要显式传递
idx
。您只需将
todo
传递给回调,并在执行
setTodos
时在列表中查找项目的索引即可。换句话说,
selectTodo
执行类似于
setTodo(prev=>[…prev.filter(\u=>\ uu.text===todo.text),todo])
。然后只需从
单todo
传递修改后的todo即可。或者,如果您知道如何在
selectTodo
方法中修改
todo
,则可以在此处进行修改。无论哪种方式,您都不需要通过索引。希望有帮助!哦,我想那就是答案了。我真的不喜欢每次我想更新一个todo时都要做一个O(N)过滤器。我更希望每个待办事项都有一个单独的状态,并将其与列表状态同步,但我想这是不可能的。谢谢你的回答!但是,如果onClick方法不在SingleTodo标记上(假设rende
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>
  );
}