ReactJS: useReducer 分发了两个不同的动作而不是一个

huangapple go评论65阅读模式
英文:

ReactJS: useReducer dispatches two different actions rather than one

问题

我试图构建一个排序算法可视化器,它在单击按钮后每秒对包含整数的数组中的元素进行一次排序。然而,每当我单击“选择排序”按钮时,它首先分派“随机化”操作类型,然后再分派“选择排序”。如果它仍然对数组进行排序,那就没问题,但它不是,相反,“选择排序”按钮在功能上充当第二个“随机化”按钮。我是否错误地使用了useReducer钩子?我如何才能使只有一个操作类型被分派?

arr-context-provider.tsx 中的代码:

import { createContext, useReducer } from "react";
import ControlPanel from './components/control-panel';
import Visualizer from './components/visualizer';

interface ArrayContextProps {
  children: React.ReactElement;
}

export type Action = 
  | { type: "selection sort" }
  | { type: "randomize" }

const initArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
export const arrContext = createContext<[number[], React.Dispatch<Action>]>([initArr, () => initArr]);

const ArrContextProvider: React.FC<ArrayContextProps> = ({ children }: ArrayContextProps) => {
  const arrReducer = (arr: number[], action: Action) => {
    switch (action.type) {
        case "selection sort":
          for (let i = 0; i < arr.length; i++) {
            setTimeout(() => {
              let minIndex = i;
              for (let j = i + 1; j < arr.length; j++) {
                if (arr[j] < arr[minIndex]) {
                  minIndex = j;
                }
              }
              if (minIndex !== i) {
                [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
              }
              
              console.log("selection sort!");
              return [...arr.slice()];
            }, 100 * (i + 1));
          }
          // 添加 break 语句
          break;

        case "randomize":
          arr = [];
          while (arr.length < 10) {
            let rng = Math.floor(Math.random() * 10) + 1;
            if (arr.indexOf(rng) === -1) {
                arr.push(rng);
            }
          }

          console.log("randomize!");
          return [...arr.slice()];
        
        default:
          console.log("error!");
          return [...arr.slice()];
    }
  }

  const [arr, dispatch] = useReducer((arr: number[], action: Action) => number[])(arrReducer, initArr);
  return (
    <arrContext.Provider value={[arr, dispatch]}>
      <div>
        <ControlPanel />
        <Visualizer />
      </div>
    </arrContext.Provider>
  );
};

export default ArrContextProvider;

control-panel.tsx 中的代码:

import { useState, useEffect, useContext } from 'react';
import { arrContext, Action } from './arr-context-provider';

const ControlPanel: React.FC = () => {
  const [arr, dispatch] = useContext<[number[], React.Dispatch<Action>]>(arrContext);
  const [algorithm, setAlgorithm] = useState("");

  useEffect(() => {
    switch(algorithm) {
      case "selection sort":
        dispatch({ type: "selection sort" });
        break;  // 添加 break 语句

      case "randomize":
        dispatch({ type: "randomize" });
        setAlgorithm("");
        break;  // 添加 break 语句

      default:
        [...arr.slice()];
    }
  }, [algorithm]);

  return (
    <div className='headerContainer'>
      <h1 className='header'>🔢🔖🔡🔣🔨🔵🔰 Sorting Algorithm Visualizer 🧉📞📲🔦</h1>
      <li className='list'>
        <ul><button className='button' onClick={() => setAlgorithm("selection sort")}>Selection Sort</button></ul>
        <ul><button className='button' onClick={() => setAlgorithm("randomize")}>Randomize</button></ul>
      </li>
    </div>
  )
}
  
export default ControlPanel;

请注意,我在代码中添加了 break 语句,以确保在每个情况下只分派一个操作类型。

英文:

I am trying to build a sorting algorithm visualizer that sorts an element of an array containing integers once every second upon clicking a button. Whenever I click the "selection sort" button, however, it dispatches the "randomize" action type first, then "selection sort." This would be fine if it still sorted the array, but it doesn't, instead the "selection sort" button functionally acts as a second "randomize" button. Am I using the useReducer hook wrong? How can I make it so that only one action type is dispatched?

arr-context-provider.tsx

import { createContext, useReducer } from &quot;react&quot;;
import ControlPanel from &#39;./components/control-panel&#39;;
import Visualizer from &#39;./components/visualizer&#39;;
interface ArrayContextProps {
children: React.ReactElement;
}
export type Action = 
| { type: &quot;selection sort&quot; }
| { type: &quot;randomize&quot; }
const initArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
export const arrContext = createContext&lt;[number[], React.Dispatch&lt;Action&gt;]&gt;([initArr, () =&gt; initArr]);
const ArrContextProvider: React.FC&lt;ArrayContextProps&gt; = ({ children }: ArrayContextProps) =&gt; {
const arrReducer = (arr: number[], action: Action) =&gt; {
switch (action.type) {
case &quot;selection sort&quot;:
for (let i = 0; i &lt; arr.length; i++) {
setTimeout(() =&gt; {
let minIndex = i;
for (let j = i + 1; j &lt; arr.length; j++) {
if (arr[j] &lt; arr[minIndex]) {
minIndex = j;
}
}
if (minIndex !== i) {
[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]];
}
console.log(&quot;selection sort!&quot;);
return [...arr.slice()];
}, 100 * (i + 1));
}
case &quot;randomize&quot;:
arr = [];
while (arr.length &lt; 10) {
let rng = Math.floor(Math.random() * 10) + 1;
if (arr.indexOf(rng) === -1) {
arr.push(rng);
}
}
console.log(&quot;randomize!&quot;);
return [...arr.slice()];
default:
console.log(&quot;error!&quot;);
return [...arr.slice()];
}
}
const [arr, dispatch] = useReducer&lt;(arr: number[], action: Action) =&gt; number[]&gt;(arrReducer, initArr);
return (
&lt;arrContext.Provider value={[arr, dispatch]}&gt;
&lt;div&gt;
&lt;ControlPanel /&gt;
&lt;Visualizer /&gt;
&lt;/div&gt;
&lt;/arrContext.Provider&gt;
);
};
export default ArrContextProvider;

control-panel.tsx

import { useState, useEffect, useContext } from &#39;react&#39;;
import { arrContext, Action } from &#39;./arr-context-provider&#39;;
const ControlPanel: React.FC = () =&gt; {
const [arr, dispatch] = useContext&lt;[number[], React.Dispatch&lt;Action&gt;]&gt;(arrContext);
const [algorithm, setAlgorithm] = useState(&quot;&quot;);
useEffect(() =&gt; {
switch(algorithm) {
case &quot;selection sort&quot;:
dispatch({ type: &quot;selection sort&quot; });
case &quot;randomize&quot;:
dispatch({ type: &quot;randomize&quot; });
setAlgorithm(&quot;&quot;);
default:
[...arr.slice()];
}
}, [algorithm]);
return (
&lt;div className=&#39;headerContainer&#39;&gt;
&lt;h1 className=&#39;header&#39;&gt;&#127362;&#127358;&#127361;&#127363;&#127352;&#127357;&#127350; &#127344;&#127355;&#127350;&#127358;&#127361;&#127352;&#127363;&#127351;&#127356; &#127365;&#127352;&#127362;&#127364;&#127344;&#127355;&#127352;&#127369;&#127348;&#127361;&lt;/h1&gt;
&lt;li className=&#39;list&#39;&gt;
&lt;ul&gt;&lt;button className=&#39;button&#39; onClick={() =&gt; setAlgorithm(&quot;selection sort&quot;)}&gt;Selection Sort&lt;/button&gt;&lt;/ul&gt;
&lt;ul&gt;&lt;button className=&#39;button&#39; onClick={() =&gt; setAlgorithm(&quot;randomize&quot;)}&gt;Randomize&lt;/button&gt;&lt;/ul&gt;
&lt;/li&gt;
&lt;/div&gt;
)
}
export default ControlPanel;

答案1

得分: 1

I'm not sure you're using useContext properly here:

const [arr, dispatch] = useContext<[number[], React.Dispatch<Action>]>(arrContext);

The dispatch function should be coming from useReducer, not useContext.

From the documentation:

import { useContext } from 'react';

function MyComponent() {
  const theme = useContext(ThemeContext);
  // ...
}
import { useReducer } from 'react';

function reducer(state, action) {
  // ...
}

function MyComponent() {
  const [state, dispatch] = useReducer(reducer, { age: 42 });
  // ...
}
英文:

I'm not sure you're using useContext properly here:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

  const [arr, dispatch] = useContext&lt;[number[], React.Dispatch&lt;Action&gt;]&gt;(arrContext);

<!-- end snippet -->

The dispatch function should be coming from useReducer not useContext.

From the documentation:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

import { useContext } from &#39;react&#39;;
function MyComponent() {
const theme = useContext(ThemeContext);
// ...

<!-- end snippet -->

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-html -->

import { useReducer } from &#39;react&#39;;
function reducer(state, action) {
// ...
}
function MyComponent() {
const [state, dispatch] = useReducer(reducer, { age: 42 });
// ...

<!-- end snippet -->

答案2

得分: 1

Your code has problem with the switch-case statements. Use break or return statements after each case. If you don't do that next case will also run. And you shouldn't use setTimeout inside of a reducer. Just return the states from reducer and then handle UI differently.

case "选择排序":
  ...你的代码,
  break;
英文:

Your code has problem with the switch-case statements. Use break or return statements after each case. If you don't do that next case will also run. And you shouldn't use setTimeout inside of a reducer. Just return the states from reducer and then handle UI differently.

case &quot;selection sort&quot;:
...your code,
break;

huangapple
  • 本文由 发表于 2023年3月31日 04:36:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/75892785.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定