如何使用 redux-thunk 调度操作让 connected-react-router 重定向?
Posted
技术标签:
【中文标题】如何使用 redux-thunk 调度操作让 connected-react-router 重定向?【英文标题】:How do I get connected-react-router to redirect using a redux-thunk dispatch action? 【发布时间】:2018-11-27 14:21:41 【问题描述】:我正在开展一个新项目。我正在使用带有 redux 和 thunk 的 create-react-app。 api 服务器是一个单独的项目,数据库使用 node/express 和 mongo/mongoose。我试图在创建或删除项目操作后重定向到项目列表视图。如果重定向在组件本身中,则重定向发生在操作完成之前,并且列表的重新获取发生在创建或删除发生之前。因此,我尝试使用 thunk 在动作的 .then() 部分中调度重定向动作。重定向成功地改变了浏览器中的 url,但没有触发重新渲染。如何从创建操作或删除操作获得重定向以触发库存列表组件的重新呈现?
完整代码在:https://github.com/jhlindell/BarCode-app/tree/stockitem 服务器在:https://github.com/jhlindell/BarCode-server/tree/jon
我的 index.js:
import 'bootstrap/dist/css/bootstrap.css';
import './index.css';
import App from './App';
import Provider from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import registerServiceWorker from './registerServiceWorker';
import ConnectedRouter from 'connected-react-router'
import createStore, applyMiddleware, compose from 'redux';
import composeWithDevTools from 'redux-devtools-extension';
import createBrowserHistory from 'history';
// import browserHistory from 'react-router'
import reducers from './reducers';
import thunk from 'redux-thunk';
import connectRouter, routerMiddleware from 'connected-react-router'
import logger from 'redux-logger';
const history = createBrowserHistory();
const reactRouterMiddleware = routerMiddleware(history);
const middleWares = [
thunk,
logger,
reactRouterMiddleware
]
const store = createStore(
connectRouter(history)(reducers),
composeWithDevTools(applyMiddleware(...middleWares)));
ReactDOM.render(
<Provider store = store>
<ConnectedRouter history=history>
<App history=history/>
</ConnectedRouter>
</Provider>
, document.getElementById('root'));
registerServiceWorker();
我的 app.js 文件:
import './App.css';
import BrowserRouter as Router, Route, Switch from 'react-router-dom';
import Footer from './components/Nav/Footer';
import NavBar from './components/Nav/NavBar';
import React, Component from 'react';
import StockItemCreate from './components/StockItems/StockItemCreate';
import StockItemDetail from './components/StockItems/StockItemDetail';
import StockItemList from './components/StockItems/StockItemList';
import HomePage from './components/HomePage';
class App extends Component
com
render()
const flexCol =
display: 'flex',
flexDirection: 'column',
;
const flex0 =
flex: 0
;
const flex1 =
display: 'flex',
flex: '1 1 100%',
;
return (
<Router>
<div className="App" style=flexCol>
<div style=flex0>
<NavBar />
</div>
<div style=flex1 id="mainBlock">
<Switch>
<Route exact path='/' component=HomePage />
<Route exact path='/stockitems/create' component=StockItemCreate />
<Route exact path='/stockitems' component=StockItemList />
<Route path='/stockitems/:id' component=StockItemDetail />
</Switch>
</div>
<div style=flex0>
<Footer />
</div>
</div>
</Router>
);
export default App;
列表组件:
import bindActionCreators from 'redux';
import connect from 'react-redux';
import React, Component from 'react';
import getStockItemList, clearStockItemList from '../../actions';
const listStyle =
display: 'flex',
margin: 'auto'
class StockItemList extends Component
componentDidMount()
this.props.getStockItemList();
componentWillUnmount()
this.props.clearStockItemList();
render()
return (
<div style=listStyle>
this.props.stockItemList ? <ul className="list-group">
this.props.stockItemList.map((item) =>
return <li
className="list-group-item"
key=item.name
onClick=()=> this.props.history.push(`/stockitems/$item._id`)
>item.name</li>
)
</ul> : <span>loading...</span>
</div>
);
function mapStateToProps(state)
return stockItemList: state.stockItemList
function mapDispatchToProps(dispatch)
return bindActionCreators( getStockItemList, clearStockItemList , dispatch)
export default connect(mapStateToProps, mapDispatchToProps)(StockItemList);
创建组件:
import bindActionCreators from 'redux';
import connect from 'react-redux';
import React, Component from 'react';
import createStockItem from '../../actions'
const cardStyle =
display: 'flex',
margin: 'auto',
;
const formStyle =
display: 'flex',
flexDirection: 'column',
width: '80%',
margin: 'auto'
;
class StockItemCreate extends Component
constructor(props)
super(props);
this.state =
name: '',
description: ''
handleFormSubmit = (event) =>
event.preventDefault();
this.props.createStockItem(this.state);
this.clearForm();
//this.props.history.push('/stockitems');
handleInputChange = (event) =>
const target = event.target;
const value = target.value;
const name = target.name;
this.setState([name]: value);
clearForm = () =>
this.setState( name: '', description: '');
render()
return(
<form className="card" onSubmit=this.handleFormSubmit style=cardStyle>
<div className="card-header">
<h3>Add new ingredient</h3>
</div>
<div className="card-block mt-2">
<div style=formStyle>
<label>Name</label>
<input name="name" type="text"
onChange=(e) => this.handleInputChange(e)
placeholder="Name"
value=this.state.name/>
</div>
<div className="mt-2" style=formStyle>
<label>Description</label>
<input name="description" type="text"
onChange=(e) => this.handleInputChange(e)
placeholder="Description"
value=this.state.description/>
</div>
</div>
<div className="btn-group mb-2 mt-2" style=padding: '0', margin: 'auto'>
<button className="btn btn-primary" type="submit">
Submit
</button>
<button className="btn btn-secondary" type="button" onClick=()=>this.clearForm()>
Cancel
</button>
</div>
</form>
)
function mapDispatchToProps(dispatch)
return bindActionCreators( createStockItem , dispatch);
export default connect(null, mapDispatchToProps)(StockItemCreate);
最后是调用重定向的动作文件:
import axios from 'axios';
import push, replace from 'connected-react-router'
const URL = 'http://localhost:8000';
export function getStockItemList()
return function(dispatch)
axios.get(`$URL/api/stock_items/`)
.then((response) =>
dispatch( type: 'STOCK_ITEM_LIST', payload: response.data );
)
.catch((error) =>
console.log('error getting stock items');
);
export function clearStockItemList()
return type: 'CLEAR_STOCK_ITEM_LIST' ;
export function getStockItemById(id)
return function(dispatch)
axios.get(`$URL/api/stock_items/$id`)
.then((response) =>
dispatch( type: 'SINGLE_STOCK_ITEM', payload: response.data );
)
.catch((error) =>
console.log('error getting stock item by id');
dispatch(push('/stockitems'));
);
export function clearSingleStockItem()
return type: 'CLEAR_SINGLE_STOCK_ITEM' ;
export function createStockItem(item)
return function(dispatch)
axios.post(`$URL/api/stock_items/`, item)
.then((response)=>
console.log("response", response);
dispatch(push('/stockitems'));
)
.catch((error) =>
//create error container to post error to
console.log('error creating stock item', error);
);
export function deleteStockItem(id)
return function(dispatch)
axios.delete(`$URL/api/stock_items/$id`)
.then((response)=>
console.log("delete response: ", response);
dispatch(push('/stockitems'));
)
.catch((error) =>
//create error container to post error to
console.log('error deleting stock item', error);
);
【问题讨论】:
【参考方案1】:考虑使用connected-react-router
中的push
作为“动作创建者”而不是this.props.history.push
。
【讨论】:
【参考方案2】:根据this SO answer:
从此改变:
<Switch>
<Route path="/">
<Welcome />
</Route>
<Redirect to="/" />
</Switch>
到这里:
<Switch>
<>
<Route path="/">
<Welcome />
</Route>
<Redirect to="/" />
</>
</Switch>
...为我工作。
我试图为任何未明确指定的路径实现基本的后备重定向,因此像 http://localhost:3000/askfjasdf 这样的随机路径将重定向到 http://localhost:3000
。出于某种原因,将片段添加为 <Switch>
的***子级就可以了。
【讨论】:
这不是正确的做法。你应该使用exact
- ***.com/questions/49162311/…以上是关于如何使用 redux-thunk 调度操作让 connected-react-router 重定向?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 redux-thunk 和 TypeScript 调度 ThunkAction
使用 Redux-Thunk / Axios 从 onUploadProgress 事件调度操作
jest redux-thunk 测试是不是调度了相同模块的操作
在React / Redux中,如果函数组件正在使用redux-thunk调度函数,则如何setIsLoading()?