如何使用redux-thunk调度操作使connected-react-router重定向?

时间:2018-06-18 17:16:23

标签: reactjs redux react-router redux-thunk

我正在开展一个新项目。我正在使用带有redux和thunk的create-react-app。 api服务器是一个单独的项目,它使用node / express和mongo / mongoose作为数据库。我想在创建或删除项目操作后重定向到项目列表视图。如果重定向位于组件本身中,则重定向在操作完成之前发生,并且在创建或删除发生之前重新发送列表。因此,我试图使用thunk在动作的.then()部分中调度重定向动作。重定向成功更改了浏览器中的URL,但未触发重新呈现。如何从create action或delete操作获取重定向以触发重新呈现stockitems列表组件?

完整代码位于: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);
      });
  }
}

2 个答案:

答案 0 :(得分:1)

请考虑使用push中的connected-react-router作为“动作创建者”,而不是this.props.history.push

答案 1 :(得分:0)

根据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>的顶级子级就可以了。