当使用react-router' history.push'导航到另一个页面时,Redux会丢失状态。

时间:2017-11-29 14:44:40

标签: react-router react-redux axios react-router-dom redux-promise

(你可以看到我的名声不是很高:)我明白,如果你不喜欢我的问题,那将是我的最后一个,因此我会把它写成尽我所能:)

我面临的问题类似于:

Redux loses state when navigating to another page

然而,上述问题的答案是使用' history.push',这就是我正在做的事情,我仍然遇到问题。

我正在使用:

  • "反应":" ^ 16.0.0"
  • " react-redux":" ^ 5.0.6"
  • " react-router":" ^ 4.2.0"
  • " react-router-dom":" ^ 4.2.2"
  • " redux":" ^ 3.7.2"
  • " Redux的-承诺":" ^ 0.5.3"
  • " axios":" ^ 0.17.1"

我正在做以下事情:

  1. 在反应组件中," SearchText",获取文本字符串并调用操作创建者
  2. 在动作创建器中,使用文本字符串向goodreads.com发送HTTP请求
  3. 在我的reducer中,使用动作有效负载设置redux状态
  4. 使用其他组件," BookResults" (在另一条路线中),以显示此状态
  5. 组件" SearchText"有一个链接到" BookResults"页。 所以,一旦" SearchText"触发动作创建者,如果(当我在控制台上看到收到结果并且状态设置有书籍列表时)我点击路由到" BookResults"的链接,我看到列表书籍。

    然而,如果" SearchText"使用(当触发动作创建者时)执行新页面的history.push的回调,并且该回调由' axios(xxx)调用。然后,状态未正确设置,尽管我看到了在控制台中,HTTP请求成功。

    我相信你可以看到我做错了什么(我希望它不是很愚蠢)...请告诉我。

    以下是代码:

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'react-redux';
    import { BrowserRouter, Route, Switch } from 'react-router-dom';
    import { createStore, applyMiddleware } from 'redux';
    import ReduxPromise from 'redux-promise';
    import SearchText from './components/search_text';
    import BookResults from './components/book_results';
    import reducers from './reducers';
    
    const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
    
    ReactDOM.render(
      <Provider store={createStoreWithMiddleware(reducers)}>
        <BrowserRouter>
        <div>
          <BrowserRouter>
            <Switch>
              <Route path="/book_results" component={BookResults} />
              <Route path="/" component={SearchText} />
            </Switch>
          </BrowserRouter>
        </div>
    
        </BrowserRouter>
      </Provider>
      , document.querySelector('#root'));
    

    SearchText组件

    import React, { Component } from 'react';
    import { connect } from 'react-redux';
    import { bindActionCreators } from 'redux';
    import { Link } from 'react-router-dom';
    import { searchForBooks } from '../actions';
    
    class SearchText extends Component {
        constructor(props) {
            super(props);
            this.state = {
                searchText: ''
            };
            this.handleFormSubmit = this.handleFormSubmit.bind(this);
            this.handleSearchTextChange = this.handleSearchTextChange.bind(this);
        }
    
        handleSearchTextChange(e) {
            this.setState({ searchText: e.target.value });
        }
    
        handleFormSubmit(e) {
            e.preventDefault();
    
            const formPayload = {
                searchText: this.state.searchText
            };
    
            console.log("In SearchBooks/handleFormSubmit. Submitting. state: ", this.state);
        this.props.searchForBooks(formPayload, () => {
                this.props.history.push(`/book_results`);
            });
        }
    
        render() {
            return (
                <form className="container" onSubmit={this.handleFormSubmit}>
                    <h3>Search Form</h3>
    
                    <div className="form-group">
                        <label className="form-label">{'Search Text:'}</label>
                        <input
                            className='form-input'
                            type='text'
                            name='searchText'
                            value={this.state.searchText}
                            onChange={this.handleSearchTextChange}
                            onBlur={this.handleSearchTextBlur}
                            placeholder='' />
                    </div>
                    <br />
                    <input
                        type="submit"
                        className="btn btn-primary float-right"
                        value="Submit"/>
                    <br /><br />
                    <Link to={`/book_results`}>&lArr; Book Results</Link>
                </form>
            );
        }
    }
    
    function mapDispatchToProps(dispatch) {
      return bindActionCreators({ searchForBooks: searchForBooks }, dispatch);
    }
    
    export default connect(null, mapDispatchToProps)(SearchText);
    

    BookResults组件

    import React from 'react';
    import { connect } from 'react-redux';
    import _ from 'lodash';
    import Book from './book';
    
    class BookResults extends React.Component {
    
      render() {
    
        let books;
        const booksArray = _.values(this.props.bookResults);
        console.log("***In BookResults. booksArray: ", booksArray);
    
        if (booksArray.length === 0) {
          books = "No books to display";
        } else {
          books = booksArray.map( (book) => {
            return (
              <Book book={book} key={book.id} />
            );
          });
        }
    
        return (
          <div>
            <h2>Search Results</h2>
            <br />
            <ul>
              {books}
            </ul>
          </div>
        );
      }
    }
    
    function mapStateToProps(state) {
      return {
        bookResults: state.bookResults,
        cats: state.cats
      };
    }
    
    export default connect(mapStateToProps)(BookResults);
    

    图书组件

    import React from 'react';
    
    const Book = (props) => (
      <li>
        {props.book.title}
      </li>
    );
    
    export default Book;
    

    动作/ index.js

    如下所示,以下行已注释掉:

    //  .then(() => callback());
    

    如果我加入它,我就有问题。

    import axios from 'axios';
    export const SEARCH_FOR_BOOKS = 'search_for_books';
    
    const GOODREADS = "https://www.goodreads.com/search/index.xml";
    const KEY = "xxx";
    
    export function searchForBooks(values, callback) {
      let result;
      console.log("In actions/searchForBooks. values: ", values);
      if (!values.searchText || values.searchText === "") {
        console.error("*** ERROR *** In actions/searchForBooks." +
        "values.searchText: ", values.searchText);
      } else {
        const searchUrl = `${GOODREADS}?key=${KEY}&q=${values.searchText}`;
        console.log("In actions/searchForBooks. url: " + searchUrl);
    
        result = axios.get(searchUrl);
        //  .then(() => callback());
      }
    
      return {
        type: SEARCH_FOR_BOOKS,
        payload: result
      };
    }
    

    减速器/ index.js

    import { combineReducers } from 'redux';
    import bookResultsReducer from './reducer_book_results';
    
    const rootReducer = combineReducers({
      bookResults: bookResultsReducer
    });
    
    export default rootReducer;
    

    减速机

    import { parseString } from  'xml2js';
    import _ from 'lodash';
    import { SEARCH_FOR_BOOKS } from '../actions/index';
    
    const bookResults = {};
    
    export default function bookResultsReducer(state = bookResults, action) {
      switch (action.type) {
        case SEARCH_FOR_BOOKS:
          console.log("In bookResultsReducer. payload: ", action.payload);
          if (action.error) {  // error from goodreads search books
            console.error("*** APP ERROR *** In bookResultsReducer. action.error: ", action.error);
          } else if (!action.payload || !action.payload.data) {
            console.error("*** APP ERROR *** In bookResultsReducer." +
            " action.payload or action.payload.data is undefined", action.payload);
          } else {
            parseString(action.payload.data, function(err, result) {
              if (err) {
                console.error("*** APP ERROR *** In bookResultsReducer. Error from parseString: ", err);
              } else {
                state = Object.assign({}, getBooks(result));
              }
            });
          }
          console.log("In bookResultsReducer. new state: ", state);
          return state;
          break;
    
        default:
          return state;
      }
    }
    
    function getBooks(data) {
    
      const bookResults =  data.GoodreadsResponse.search[0].results[0].work;
      if (!bookResults || bookResults.length === 0) {
        return {};
      } else {
        const results = bookResults.map( (book, index) => {
          const bookInfo = book.best_book[0];
          return (
            { id: index + 1,
              title: bookInfo.title[0] }
          );
        });
        return _.mapKeys(results, 'id');
      }
    }
    

1 个答案:

答案 0 :(得分:1)

有人通过邮件向我发送了解决方案。

错误发生在actions / index.js文件中。

而不是:

import axios from 'axios';
export const SEARCH_FOR_BOOKS = 'search_for_books';

const GOODREADS = "https://www.goodreads.com/search/index.xml";
const KEY = "xxx";

export function searchForBooks(values, callback) {
  let result;
  console.log("In actions/searchForBooks. values: ", values);
  if (!values.searchText || values.searchText === "") {
    console.error("*** ERROR *** In actions/searchForBooks." +
    "values.searchText: ", values.searchText);
  } else {
    const searchUrl = `${GOODREADS}?key=${KEY}&q=${values.searchText}`;
    console.log("In actions/searchForBooks. url: " + searchUrl);

    result = axios.get(searchUrl)
      .then(() => callback());
  }

  return {
    type: SEARCH_FOR_BOOKS,
    payload: result
  };
}

我应该写:

import axios from 'axios';
export const SEARCH_FOR_BOOKS = 'search_for_books';

const GOODREADS = "https://www.goodreads.com/search/index.xml";
const KEY = "xxx";

export function searchForBooks(values, callback) {
  let result;
  console.log("In actions/searchForBooks. values: ", values);
  if (!values.searchText || values.searchText === "") {
    console.error("*** ERROR *** In actions/searchForBooks." +
    "values.searchText: ", values.searchText);
  } else {
    const searchUrl = `${GOODREADS}?key=${KEY}&q=${values.searchText}`;
    console.log("In actions/searchForBooks. url: " + searchUrl);

    result = axios.get(searchUrl)
      .then((res) => {
        callback();
        return res;
      });
  }

  return {
    type: SEARCH_FOR_BOOKS,
    payload: result
  };
}

说明:

问题是 axios.get 返回的值会传递给 .then 子句,以及从 .then子句返回的内容设置为结果的值。 我的错误是我没有从 .then子句返回任何内容,因此结果的值未定义,而不是返回的promise。