1

(你可以看到我的声誉不是很高:) 我知道如果你不喜欢我的问题,这将是我的最后一个问题,因此我会尽可能地写它:)

我面临的问题类似于:

Redux 在导航到另一个页面时丢失状态

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

我在用:

  • “反应”:“^16.0.0”
  • “反应减少”:“^5.0.6”
  • “反应路由器”:“^4.2.0”
  • “反应路由器 dom”:“^4.2.2”
  • “还原”:“^3.7.2”
  • "redux-promise":"^0.5.3"
  • “axios”:“^0.17.1”

我正在执行以下操作:

  1. 在反应组件“SearchText”中,获取文本字符串并调用动作创建者
  2. 在 action creator 中,使用文本字符串向 goodreads.com 发送 HTTP 请求
  3. 在我的 reducer 中,使用 action payload 设置 redux 状态
  4. 使用另一个组件“BookResults”(在另一个路由中)来显示此状态

组件“SearchText”有一个指向“BookResults”页面的链接。因此,一旦“SearchText”触发动作创建者,如果(当我在控制台上看到收到结果并且状态设置为书籍列表时)我单击路由到“BookResults”的链接,我看到书籍清单。

但是,如果“SearchText”使用(在触发动作创建者时)执行新页面的 history.push 的回调,并且此回调由 'axios(xxx).then' 调用,则状态设置不正确,尽管我在控制台中看到 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');
  }
}
4

1 回答 1

2

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

错误出现在 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子句返回的任何内容都设置为result的值。我的错误是我没有从.then 子句返回任何内容,因此结果的值是未定义的,而不是返回的承诺。

于 2017-12-08T09:30:24.550 回答