如何在React.js中实现待办组件的随机顺序展示?
实现Todo组件的随机顺序展示
核心思路
- 预先生成不重复的随机Todo顺序列表,可基于
userId保证同一用户的顺序固定(避免刷新后打乱) - 用
currentIndex跟踪当前展示的Todo位置,替代原有的硬编码TodoState字符串 - 统一管理组件映射关系,简化渲染逻辑
- 子组件通过上下文调用通用的切换方法,不再硬编码目标状态
具体实现步骤
1. 父组件(Todo.jsx)代码修改
补全缺失组件导入,重构状态与逻辑:
import TodoOne from "./Todos/TodoOne"; import TodoTwo from "./Todos/TodoTwo"; import TodoThree from "./Todos/TodoThree"; import TodoFour from "./Todos/TodoFour"; import TodoEight from "./Todos/TodoEight"; import TodoSeven from "./Todos/TodoSeven"; import TodoSix from "./Todos/TodoSix"; import TodoFive from "./Todos/TodoFive"; import TodoNine from "./Todos/TodoNine"; import TodoTen from "./Todos/TodoTen"; import { SurveyContext } from "../Helpers/Contexts.js"; import { TodoContext } from "../Helpers/TodosContext"; import { generateStringPair, generateParagraphPair } from "../stringManager.js"; import TodoEleven from "./Todos/TodoEleven"; import TodoTwelve from "./Todos/TodoTwelve"; import TodoThirteen from "./Todos/TodoThirteen"; import TodoFourteen from "./Todos/TodoFourteen"; import TodoFifteen from "./Todos/TodoFifteen"; function Todos() { const type1 = "sidebyside"; const type2 = "topofeachother"; const { userId } = useContext(SurveyContext); // 定义所有Todo组件的配置映射 const todoConfig = [ { component: TodoOne, type: type2, itemIndex: 0 }, { component: TodoTwo, type: type1, itemIndex: 1 }, { component: TodoThree, type: type2, itemIndex: 2 }, { component: TodoFour, type: type2, itemIndex: 3 }, { component: TodoFive, type: type2, itemIndex: 4 }, { component: TodoSix, type: type1, itemIndex: 5 }, { component: TodoSeven, type: undefined, itemIndex: 6 }, { component: TodoEight, type: undefined, itemIndex: 7 }, { component: TodoNine, type: undefined, itemIndex: 8 }, { component: TodoTen, type: undefined, itemIndex: 9 }, { component: TodoEleven, type: undefined, itemIndex: 10 }, { component: TodoTwelve, type: undefined, itemIndex: 11 }, { component: TodoThirteen, type: type1, itemIndex: 12 }, { component: TodoFourteen, type: undefined, itemIndex: 13 }, { component: TodoFifteen, type: undefined, itemIndex: 14 }, ]; // 生成可复现的随机顺序(基于userId) const generateRandomOrder = (seed) => { const order = [...Array(todoConfig.length).keys()]; let currentIndex = order.length; let randomIndex; // 伪随机数生成,保证同一seed输出相同顺序 const random = () => { seed = (seed * 1103515245 + 12345) % 2**31; return seed / 2**31; }; while (currentIndex !== 0) { randomIndex = Math.floor(random() * currentIndex); currentIndex--; [order[currentIndex], order[randomIndex]] = [order[randomIndex], order[currentIndex]]; } return order; }; // 初始化随机顺序(组件挂载时生成一次) const [randomTodoOrder] = useState(() => generateRandomOrder(userId || Date.now())); // 跟踪当前展示的Todo索引 const [currentIndex, setCurrentIndex] = useState(0); const totalTodos = todoConfig.length; // 切换到下一个Todo的通用方法 const nextTodo = () => { if (currentIndex < totalTodos - 1) { setCurrentIndex(prev => prev + 1); } else { console.log("所有Todo已完成"); // 可添加完成后的跳转逻辑 } }; // 上下文状态 const state = { currentIndex, totalTodos, nextTodo, }; // --- 原有文本生成逻辑保持不变 --- const [text1_1, text2_1] = generateStringPair(5, "randomChange"); const [text1_2, text2_2] = generateStringPair(15, "match"); const [text1_3, text2_3] = generateStringPair(5, "hardChange"); const [text1_4, text2_4] = generateStringPair(10, "match"); const [text1_5, text2_5] = generateStringPair(15, "randomChange"); const [text1_6, text2_6] = generateStringPair(15, "hardChange"); const [text1_7, text2_7] = generateStringPair(5, "randomChange"); const [text1_8, text2_8] = generateStringPair(15, "match"); const [text1_9, text2_9] = generateStringPair(5, "hardChange"); const [text1_10, text2_10] = generateStringPair(10, "match"); const [text1_11, text2_11] = generateStringPair(15, "randomChange"); const [text1_12, text2_12] = generateStringPair(15, "hardChange"); const [text1_13, text2_13] = generateParagraphPair("simplerror"); const [text1_14, text2_14] = generateParagraphPair("simplerror"); const [text1_15, text2_15] = generateParagraphPair("harderror"); const [items] = useState([ { userId, TodoId: 1, text1: text1_1, text2: text2_1 }, { userId, TodoId: 2, text1: text1_2, text2: text2_2 }, { userId, TodoId: 3, text1: text1_3, text2: text2_3 }, { userId, TodoId: 4, text1: text1_4, text2: text2_4 }, { userId, TodoId: 5, text1: text1_5, text2: text2_5 }, { userId, TodoId: 6, text1: text1_6, text2: text2_6 }, { userId, TodoId: 7, text1: text1_7, text2: text2_7 }, { userId, TodoId: 8, text1: text1_8, text2: text2_8 }, { userId, TodoId: 9, text1: text1_9, text2: text2_9 }, { userId, TodoId: 10, text1: text1_10, text2: text2_10 }, { userId, TodoId: 11, text1: text1_11, text2: text2_11 }, { userId, TodoId: 12, text1: text1_12, text2: text2_12 }, { userId, TodoId: 13, text1: text1_13, text2: text2_13 }, { userId, TodoId: 14, text1: text1_14, text2: text2_14 }, { userId, TodoId: 15, text1: text1_15, text2: text2_15 }, ]); // 获取当前要渲染的Todo配置 const currentTodo = todoConfig[randomTodoOrder[currentIndex]]; const TodoComponent = currentTodo.component; const currentItem = items[currentTodo.itemIndex]; return ( <TodoContext.Provider value={state}> <TodoComponent type={currentTodo.type} text1={currentItem.text1} text2={currentItem.text2} /> </TodoContext.Provider> ); } export default Todos;
2. 子组件(以TodoOne为例)修改
去掉硬编码的状态切换,改用上下文提供的通用方法:
import { TodoContext } from "../../Helpers/TodosContext"; import ProgressBar from "../ProgressBar"; import "./Todos.css"; const TodoOne = (props) => { const { currentIndex, totalTodos, nextTodo } = useContext(TodoContext); return ( <section className="section_Todos"> <ProgressBar currentTodoIndex={currentIndex + 1} totalTodosCount={totalTodos} /> <br /> <br /> <main className="optional_Todos"> <h1>{props.text1}</h1> <h1>{props.text2}</h1> </main> <div className="text-center button-container"> <button className="btn btn-primary d-inline-flex flex-row align-items-center" onClick={nextTodo} > Next </button> <br /> <br /> </div> </section> ); }; export default TodoOne;
关键说明
- 顺序稳定性:用
userId作为洗牌种子,同一用户每次打开页面顺序一致;若需每次刷新随机,直接用Date.now()替代userId即可。 - 可维护性:通过组件映射表避免了大量条件判断,新增Todo只需在
todoConfig中添加配置。 - 低耦合:子组件无需关心下一个Todo的具体状态,只需调用通用方法,降低了组件间的依赖。
内容的提问来源于stack exchange,提问作者MexcelsiorB
相关产品推荐
相关产品推荐

