简体   繁体   English

如何使用 redux-thunk 调度操作让连接反应路由器重定向?

[英]How do I get connected-react-router to redirect using a redux-thunk dispatch action?

I'm getting a new project going.我要开始一个新项目。 I am using create-react-app with redux and thunk.我正在使用带有 redux 和 thunk 的 create-react-app。 The api server is a separate project, and uses node/express and mongo/mongoose for the database. api server 是一个单独的项目,数据库使用 node/express 和 mongo/mongoose。 I am trying to redirect to an item list view after either a create or delete item action.我试图在创建或删除项目操作后重定向到项目列表视图。 If the redirect is in the component itself, the redirect happens before the action is completed, and the refetch of the list happens before the create or delete happens.如果重定向在组件本身中,则重定向发生在动作完成之前,列表的重新获取发生在创建或删除发生之前。 Therefore I am trying to use thunk to dispatch a redirect action in the .then() part of the action.因此,我尝试使用 thunk 在动作的 .then() 部分调度重定向动作。 The redirect successfully changes the url in the browser, but no re-render is triggered.重定向成功更改了浏览器中的 url,但不会触发重新渲染。 How can I get the redirect to from either the create action or delete action to trigger a re-render of the stockitems list component?如何从创建操作或删除操作获取重定向以触发 stockitems 列表组件的重新呈现?

full code is at: https://github.com/jhlindell/BarCode-app/tree/stockitem server is at: https://github.com/jhlindell/BarCode-server/tree/jon完整代码位于: https : //github.com/jhlindell/BarCode-app/tree/stockitem服务器位于: https : //github.com/jhlindell/BarCode-server/tree/jon

My index.js:我的 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();

My app.js file:我的 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;

the list component:列表组件:

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);

the create component:创建组件:

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);

and finally, the action file where the redirect is called:最后,调用重定向的操作文件:

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);
      });
  }
}

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

As per this SO answer :根据这个 SO 答案

Changing from this:从此改变:

    <Switch>
      <Route path="/">
        <Welcome />
      </Route>
      <Redirect to="/" />
    </Switch>

To this:对此:

    <Switch>
      <>
        <Route path="/">
          <Welcome />
        </Route>
        <Redirect to="/" />
      </>
    </Switch>

... worked for me. ...为我工作。

I was trying to implement a basic fallback redirect for any paths not explicitly specified, so random paths like http://localhost:3000/askfjasdf would redirect to http://localhost:3000 .我试图为未明确指定的任何路径实现基本的回退重定向,因此像 http://localhost:3000/askfjasdf 这样的随机路径将重定向到http://localhost:3000 For some reason adding the fragment as a top level child of <Switch> did the trick.出于某种原因,将片段添加为<Switch>的顶级子级就成功了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM