(Next.js) Thunk 不会触发 reducer
Posted
技术标签:
【中文标题】(Next.js) Thunk 不会触发 reducer【英文标题】:(Next.js) Thunk does not trigger reducer 【发布时间】:2020-01-15 05:29:34 【问题描述】:我使用 next.js (https://github.com/zeit/next.js/tree/canary/examples/with-redux) 提供的 with-redux 示例在 next.js 中设置了 React-Redux。此外,我已经设置了 thunk,因为将来大多数 redux 调用将是异步的(现在它只是初始状态,将被更改)。
当我现在尝试使用 thunk 调度一个函数时,reducer 永远不会被触发。
现在我已经了解如何完成这项工作了。 (注意:这是我第一次尝试使用 next 设置 redux,直到现在我只通过具有清晰路由结构的客户端应用程序完成)
menuStatusActions.js
import * as types from "./actionTypes";
export function changeMenu(id)
return type: types.MENU_CHANGE, id ;
export function changeMenuStatus(id)
return function(dispatch)
debugger;
return dispatch(changeMenu(id));
;
menuStatusReducer.js
import * as types from "../actions/actionTypes";
import initialState from "./initialState";
export default function menuStatusReducer(
state = initialState.menuState,
action
)
switch (action.type)
case types.MENU_CHANGE:
return Object.assign([], state,
[action.id - 1]: !state[action.id - 1]
);
default:
return state;
configureStore.js
import createStore, applyMiddleware from "redux";
import composeWithDevTools from "redux-devtools-extension";
import thunkMiddleware from "redux-thunk";
import reduxImmutableStateInvariant from "redux-immutable-state-invariant";
import rootReducer from "./reducers";
import inState from "./reducers/initialState";
export default function initializeStore(initialState = inState)
return createStore(
rootReducer,
initialState,
composeWithDevTools(
applyMiddleware(thunkMiddleware, reduxImmutableStateInvariant())
)
);
with-redux-store.js(从上面的 next.js github 复制[并改编])
/* eslint-disable no-param-reassign */
/* eslint-disable react/jsx-props-no-spreading */
/* eslint-disable react/prop-types */
/* eslint-disable no-underscore-dangle */
/* eslint-disable-next-line no-param-reassign */
import React from "react";
import initializeStore from "./redux/configureStore";
const isServer = typeof window === "undefined";
const __NEXT_REDUX_STORE__ = "__NEXT_REDUX_STORE__";
function getOrCreateStore(initialState)
// Always make a new store if server, otherwise state is shared between requests
if (isServer)
return initializeStore(initialState);
// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__])
window[__NEXT_REDUX_STORE__] = initializeStore(initialState);
return window[__NEXT_REDUX_STORE__];
export default App =>
return class AppWithRedux extends React.Component
static async getInitialProps(appContext)
// Get or Create the store with `undefined` as initialState
// This allows you to set a custom default initialState
const reduxStore = getOrCreateStore();
// Provide the store to getInitialProps of pages
appContext.ctx.reduxStore = reduxStore;
let appProps = ;
if (typeof App.getInitialProps === "function")
appProps = await App.getInitialProps(appContext);
return
...appProps,
initialReduxState: reduxStore.getState()
;
constructor(props)
super(props);
this.reduxStore = getOrCreateStore(props.initialReduxState);
render()
return <App ...this.props reduxStore=this.reduxStore />;
;
;
_app.js(从上面的 next.js 示例 github 复制)
import App from "next/app";
import React from "react";
import Provider from "react-redux";
import withReduxStore from "../src/with-redux-store";
class MyApp extends App
render()
const Component, pageProps, reduxStore = this.props;
return (
<Provider store=reduxStore>
<Component ...pageProps />
</Provider>
);
export default withReduxStore(MyApp);
调用商店的组件(所有页面使用的***组件)
import React from "react";
import connect from "react-redux";
import makeStyles from "@material-ui/core/styles";
import Drawer from "@material-ui/core/Drawer";
import CssBaseline from "@material-ui/core/CssBaseline";
import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar";
import List from "@material-ui/core/List";
import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider";
import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText";
import Collapse from "@material-ui/core";
import PropTypes from "prop-types";
import Link from "next/link";
import sideMenuItems from "./sideMenuItems";
import changeMenuStatus from "../redux/actions/menuStatusActions";
const drawerWidth = 240;
const useStyles = makeStyles(theme => (
root:
display: "flex"
,
appBar:
width: `calc(100% - $drawerWidthpx)`,
marginLeft: drawerWidth
,
drawer:
width: drawerWidth,
flexShrink: 0
,
drawerPaper:
width: drawerWidth
,
toolbar: theme.mixins.toolbar,
content:
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing(3)
));
function Layout( title, menuState, changeMenuStatusAction )
const classes = useStyles();
const open = menuState;
return (
<div className=classes.root>
<CssBaseline />
<AppBar position="fixed" className=classes.appBar>
<Toolbar>
<Typography variant="h6" noWrap>
title
</Typography>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes=
paper: classes.drawerPaper
anchor="left"
>
<div className=classes.toolbar />
<Divider />
<List>
sideMenuItems.map(item => (
<>
<ListItem
key=`item$item.id`
button
onClick=() => changeMenuStatusAction(item.id)
>
<ListItemText primary=item.title />
</ListItem>
<Collapse
key=`collapse$item.id`
in=open[item.id - 1]
timeout="auto"
>
<List component="div" disablePadding key=`List$item.id`>
item.children.map(childItem => (
<Link
key=`Link$childItem.id`
href=`$item.href$childItem.href`
>
<ListItem button key=`ListItem$childItem.id`>
<ListItemText secondary=childItem.title />
</ListItem>
</Link>
))
</List>
</Collapse>
</>
))
</List>
</Drawer>
<main className=classes.content>
<div className=classes.toolbar />
Test 1234!
</main>
</div>
);
Layout.propTypes =
title: PropTypes.string.isRequired,
// eslint-disable-next-line react/forbid-prop-types
menuState: PropTypes.any.isRequired,
changeMenuStatusAction: PropTypes.func.isRequired
;
function mapStateToProps(state)
return
menuState: state.menuState
;
const mapDispatchToProps = dispatch => (
changeMenuStatusAction: dispatch(changeMenuStatus)
);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Layout);
结果没有错误,但基本上发生的事情什么都没有。试图在 thunk 中添加一个“调试器”,但它从未被触发。
它必须是异步的才能工作吗?据我了解,thunkMiddleware 不会关心传递的函数是否同步。
非常感谢所有的帮助!
【问题讨论】:
+注意:如果我尝试通过 chrome 中的 REDUX 开发工具手动调度操作,则操作按预期工作。因此,我认为减速器按预期工作。 【参考方案1】:mapDispatchToProps
应该是一个输入为dispatch
的函数,然后你必须用dispatch
包装你的操作。将您的mapDispatchToProps
更改为关注
const mapDispatchToProps = (dispatch) => (
changeMenuStatusAction: (id) => dispatch(changeMenuStatus(id))
);
然后您可以使用changeMenuStatusAction
作为道具并使用它来触发您的操作。希望它有所帮助:)。
【讨论】:
这会在操作中出现:TypeError: dispatch is not a function
错误。 thunk 被调用的地方。 -> 我想这很好,因为 thunk 被击中了。
TypeError: dispatch is not a function (anonymous function) ./src/redux/actions/menuStatusActions.js:14
我在 thunk 中添加了一个调试器。现在在这里 dispatch = 1 (这是传递给它的 id)
更新您的操作以返回调度而不是 1
我做了 - 最初是为了测试。 export function changeMenuStatus(id) return function(dispatch) debugger; return dispatch(changeMenu(id)); ;
以上是关于(Next.js) Thunk 不会触发 reducer的主要内容,如果未能解决你的问题,请参考以下文章
在调用另一个动作创建者之前等待动作创建者完成 - Redux thunk
Next.js Router.push() 自动重新加载页面