【问题标题】:React-Redux: What is the canonical way to bind a keypress action to kick off a reducer sequence?React-Redux:绑定按键操作以启动 reducer 序列的规范方法是什么?
【发布时间】:2017-01-17 09:43:06
【问题描述】:
这是 react-redux 的一个新手问题,我花了几个小时四处寻找才找到,所以我发布了这个问题,然后为后代回答,也许还有代码审查。
我正在使用 react-redux 创建一个游戏,我想使用 WASD 键在小地图上移动角色。 (这只是一个更大努力的实践示例)。地图只是由一堆彩色的<div>s 组成。
据我了解,我需要以某种方式将 keypress 事件绑定到 React DOM 中的某些内容,以便触发 mapDispatchToProps,然后开始重新评估减速器。问题是,这是一个按键,没有什么可以绑定的。我正在使用 jquery 来绑定按键并调用函数。
相关查询:
【问题讨论】:
标签:
reactjs
redux
react-redux
【解决方案1】:
您基本上可以在keypress 事件处理程序中触发操作
class App extends React.Component {
constructor() {
super();
this.handleKeyPress = this.handleKeyPress.bind(this);
}
handleKeyPress(event) {
// you may also add a filter here to skip keys, that do not have an effect for your app
this.props.keyPressAction(event.keyCode);
}
componentDidMount() {
document.addEventListener('keypress', this.handleKeyPress);
}
componentWillUnmount() {
document.removeEventListener('keypress', this.handleKeyPress);
}
render() {
return <div>Your game content</div>;
}
}
export default connect(mapStateToProps, {keyPressAction})(App)
handleKeyPress 调用动作创建者,这会将动作下推到减速器。
【解决方案2】:
对于那些在带有函数类的 typescript 中使用更新的 redux 的人,你可能想要这样的东西:
import { useDispatch } from "react-redux";
import { useEffect } from "react";
const App = () => {
const dispatch = useDispatch();
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
return function cleanup() {
document.removeEventListener('keydown', handleKeyDown);
document.removeEventListener('keyup', handleKeyUp);
};
});
const handleKeyDown = (event) => {
console.debug("Key event", event);
dispatch(handleKeyInput(game_state, connection_status, event.key));
document.removeEventListener('keydown', handleKeyDown);
};
const handleKeyUp = (event) => {
document.addEventListener('keydown', handleKeyDown, {once: true});
};
}
见https://reactjs.org/docs/hooks-effect.html。
【解决方案3】:
解决方案改编自这里:
addEventListener react redux with mapped dispatch
关键是删除 jquery 并使用 document.addEventListener 将其绑定到 react 组件中。这是工作代码的摘录:
////////////////////////////////////////////
////////////////////// containers
////////////////////////////////////////////
class GameMap extends React.Component{
renderMap(){
console.log('renderMap')
console.log(this.props)
return this.props.gamemap.map((tile) => {
//const x = "tile " + tile
return <div className={"tile " + tile}></div>
})
}
render() {
console.log('GameMap.render()')
return (
<div className="GameMap">
{this.renderMap()}
</div>)
}
componentDidMount() {
console.log("componentDidMount")
console.log(this)
// the following line won't be bound to the store here...
document.addEventListener("keydown", this.props.keyPress );
}
}
function GMmapStateToProps(state){
//from here goes into this.props
console.log('BLmapStateToProps')
console.log(state)
const gamemap = state.gamemap.gamemap.map((a) => {
switch (a){
case 1:
return "tile-free"
case 9:
return "tile-user"
}
return "tile-wall"
})
return{
gamemap: gamemap
}
}
function GMmapDispatchToProps(dispatch){
//when selectbook called, pass result to all reducers
console.log('GMmapDispatchToProps')
return bindActionCreators({keyPress: keyPress}, dispatch)
}
const VGameMap = connect(GMmapStateToProps, GMmapDispatchToProps)(GameMap)
////////////////////////////////////////////
////////////////////// actions
////////////////////////////////////////////
// actions/index.js action creator
function keyPress(key) {
console.log('keyPress: ', key)
console.log(key.key)
var vector = ""
switch(key){
case 'w', 'ArrowUp':
vector = {x:0,y:1}
case 's', 'ArrowDown':
vector = {x:0,y:-1}
case 'a', 'ArrowLeft':
vector = {x:-1,y:0}
case 'd', 'ArrowRight':
vector = {x:1,y:0}
}
return {
type: "KEYPRESS",
payload: vector
} // this is an action created
}