Javascript 如何使用React上下文更新列表中的单个项目?
我真的很喜欢react上下文,但我认为它缺少了一些东西(或者我不知道如何做) 假设我有一个TODO列表,它对应的提供者为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
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>
);
}