英文:
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 "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));
}
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" });
case "randomize":
dispatch({ type: "randomize" });
setAlgorithm("");
default:
[...arr.slice()];
}
}, [algorithm]);
return (
<div className='headerContainer'>
<h1 className='header'>🆂🅾🆁🆃🅸🅽🅶 🅰🅻🅶🅾🆁🅸🆃🅷🅼 🆅🅸🆂🆄🅰🅻🅸🆉🅴🆁</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;
答案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<[number[], React.Dispatch<Action>]>(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 'react';
function MyComponent() {
const theme = useContext(ThemeContext);
// ...
<!-- end snippet -->
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-html -->
import { useReducer } from 'react';
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 "selection sort":
...your code,
break;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论