javascript - 如何在 ReactJS 的 Promise 中解析 useReducer 的调度函数
问题描述
我正在构建一个带有反应的简单鼓机应用程序,并且我正在使用带有钩子和 useReducer 的 Context Api。应用程序的初始状态是电源键设置为 false 的对象。
基本上我想要实现的是;当电源“打开”(真)时,它会发送一个动作来显示一个闪烁的“-LOADING PRESETS-”,持续很短的时间,比如 2 秒,然后触发另一个调度动作来显示默认设置,比如“鼓”不眨眼
当电源关闭时,它会将所有内容重置为初始状态。我将 useEffect 与 2 个函数一起使用,这些函数返回一个模拟异步步骤的承诺。
我几乎把所有事情都做好了;除了加载功能外,鼓垫、音频和灯光都可以正常工作。问题是它跳过显示闪烁的“加载预设”并自动解决以显示闪烁的默认设置“鼓”(它不应该)。我正在附加一个类以使文本闪烁并在 2 秒延迟到期后删除该类。我不确定如何以及在哪里清理/清除 setTimeout,所以我现在将其注释掉。需要帮忙!
这是 App.js 代码:
const initialState = {
power: false,
display: '...',
sound_mode: '...',
volume_lvl: 0.4
};
const stateReducer = (state = initialState, action) => {
switch (action.type) {
case 'TOGGLE_POWER':
return { ...state, power: !state.power };
case 'ADJUST_VOLUME':
return { ...state, volume_lvl: action.volume };
case 'SWITCH_TO_SYNTH_MODE':
return {
...state,
display: action.display,
sound_mode: action.sound_mode
};
case 'SWITCH_TO_DRUM_MODE':
return {
...state,
display: action.display,
sound_mode: action.sound_mode
};
case 'CHANGE_DISPLAY':
return { ...state, display: action.display };
case 'LOAD_SETTINGS':
return {
...state,
display: 'Kick',
sound_mode: 'Drums',
volume_lvl: 0.4
};
case 'RESET_DEFAULTS':
return {
...state,
power: false,
display: '...',
sound_mode: '...',
volume_lvl: 0.4
};
default:
return state;
}
};
function App() {
const [appState, dispatch] = useReducer(stateReducer, initialState);
return (
<AppContext.Provider value={{ appState, dispatch }}>
<div className='DrumApp'>
<Header />
<DrumInterface />
<Footer />
</div>
</AppContext.Provider>
);
}
电源按钮所在的标题部分:
function Header() {
const { appState, dispatch } = useContext(AppContext);
const { power } = appState;
const powerAudioRef = useRef();
const displayRef = useRef();
useEffect(() => {
function loadPrompt(power) {
return new Promise((resolve, reject) => {
if (power === true) {
displayRef.current = setTimeout(() => {
resolve([
dispatch({
type: 'CHANGE_DISPLAY',
display: '--loading preset--'
}),
document.getElementById('preset-status').classList.add('loading')
]);
}, 2000);
}
});
}
function loadSettings() {
return new Promise((resolve, reject) => {
resolve([
//document.getElementById('preset-status').classList.remove('loading'),
dispatch({ type: 'LOAD_SETTINGS' }),
clearTimeout(displayRef.current)
]);
});
}
async function loadPresetSettings() {
await loadPrompt(power);
await loadSettings();
}
loadPresetSettings();
// return () => {
// clearTimeout(displayRef.current);
// };
}, [power, dispatch]);
useEffect(() => {
if (power === false) {
dispatch({ type: 'RESET_DEFAULTS' });
}
}, [power, dispatch]);
const handlePower = () => {
dispatch({ type: 'TOGGLE_POWER' });
handlePowerSound();
};
const handlePowerSound = () => {
document.getElementById('power-btn-audio').play();
document.getElementById('power-btn-audio').currentTime = 0;
};
return (
<header id='App-header'>
<div id='logo'>
<h1>
<span className='orange'>Drum</span>Machine
</h1>{' '}
<p className='small'>
Powered by:{' '}
<FaReact
className={power === true ? 'react-icon spin' : 'react-icon'}
/>{' '}
React
</p>
</div>
<div id='power'>
<button
id='power-btn'
className={power === true ? 'on' : null}
onClick={handlePower}
>
<FaPowerOff />
</button>
<audio
ref={powerAudioRef}
id='power-btn-audio'
src='https://res.cloudinary.com/dzsmdyknz/video/upload/v1532750168/sample-swap/sfx-and-unusual-sounds/bleeps-blips-blonks-blarts-and-zaps/boip.mp3'
className='btn-audio-efx'
>
Your browser does not support the audio element.
</audio>
</div>
</header>
);
}
解决方案
更新:
最后,我能够解决模拟异步进程的问题,这是我的代码:
useEffect(() => {
/* Display the loading preset and add a classname to make it blink */
function promptLoadingStart() {
return new Promise((resolve, reject) => {
let wait = setTimeout(() => {
dispatch({ type: 'CHANGE_DISPLAY', display: '--loading preset--' });
document.getElementById('preset-status').classList.add('loading');
clearTimeout(wait);
resolve('prompt loading started');
}, 2000);
});
}
/* Remove the classname for blinking text */
function prompLoadingEnd() {
return new Promise((resolve, reject) => {
let wait = setTimeout(() => {
document.getElementById('preset-status').classList.remove('loading');
clearTimeout(wait);
resolve('prompt loading ended');
}, 1000);
});
}
/* Display the initial settings */
function loadSettings() {
return new Promise((resolve, reject) => {
let wait = setTimeout(() => {
dispatch({ type: 'LOAD_SETTINGS' });
handlePowerSound();
clearTimeout(wait);
resolve('load settings done');
}, 200);
});
}
async function loadPreset() {
const step_one = await promptLoadingStart();
console.log(step_one);
const step_two = await prompLoadingEnd();
console.log(step_two);
const step_three = await loadSettings();
console.log(step_three);
}
if (power === true) {
loadPreset();
}
}, [power, dispatch]);
伙计们,如果您有更好的解决方案,我想知道如何。谢谢!
推荐阅读
- android - Facebook Android SDK:通过应用范围的 userId 获取用户
- php - 如何在 Laravel 中为 Pivot 模型用户路由模型绑定
- typescript - 将 Next.js 与 typescript 一起使用,但不能在 WithRouterProps 中应用泛型
- javascript - 在 reactJs 中使用 id 滚动到特定 div 而不使用任何其他库
- android - Java.Lang.NoSuchMethodError:没有非静态方法“Landroid/content/Context;.getColorStateList(I)Landroid/content/res/ColorStateList;”
- angular - Angular 6 - 使用await for http客户端,如果失败我如何获得状态码
- svg - SVG动画查询
- solr - 如何使用 TZ 参数在 solr 中获取文字日期范围查询
- javascript - 尝试有条件地映射对象数组
- c - sqlite3 - 如何知道读取的行是最后一行