You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React.js中实现待办组件的随机顺序展示?

实现Todo组件的随机顺序展示

核心思路

  1. 预先生成不重复的随机Todo顺序列表,可基于userId保证同一用户的顺序固定(避免刷新后打乱)
  2. 用currentIndex跟踪当前展示的Todo位置,替代原有的硬编码TodoState字符串
  3. 统一管理组件映射关系,简化渲染逻辑
  4. 子组件通过上下文调用通用的切换方法,不再硬编码目标状态

具体实现步骤

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 18:17:09