(as you can see my reputation is not very high :) and I understand that if you don't like my question it is going to be my last one, therefore I am going to write it as good as I can :)
The problem I am facing is a similar to:
Redux loses state when navigating to another page
However, the answer to the above question was to use 'history.push', which is what I am doing, and I am still having a problem.
I am using:
- "react": "^16.0.0"
- "react-redux": "^5.0.6"
- "react-router": "^4.2.0"
- "react-router-dom": "^4.2.2"
- "redux": "^3.7.2"
- "redux-promise":"^0.5.3"
- "axios": "^0.17.1"
I am doing the following:
- In a react component, "SearchText", getting a text string and calling an action creator
- In the action creator, using the text string to send an HTTP request to goodreads.com
- In my reducer, using the action payload to set the redux state
- Using another component, "BookResults" (in another route), to display this state
The component "SearchText" has a link to the "BookResults" page. So, once "SearchText" fires the action creator, if (when I see on the console that a result is received and the state is set with a list of books) I click on the link that routes to "BookResults", I see the list of books.
If, however, "SearchText" uses (when firing the action creator) a callback that performs history.push of the new page, and this callback is called by 'axios(xxx).then', the state is not set properly, although I see in the console that the HTTP request was successful.
I am sure you can see what I am doing wrong (and I hope it is not very stupid)... Please tell me.
Here is the code:
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 component
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`}>⇐ Book Results</Link>
            </form>
        );
    }
}
function mapDispatchToProps(dispatch) {
  return bindActionCreators({ searchForBooks: searchForBooks }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchText);
BookResults component
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);
Book component
import React from 'react';
const Book = (props) => (
  <li>
    {props.book.title}
  </li>
);
export default Book;
actions/index.js
As you can see below, the following line is commented out:
//  .then(() => callback());
If I include it, I have the problem.
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
  };
}
reducers/index.js
import { combineReducers } from 'redux';
import bookResultsReducer from './reducer_book_results';
const rootReducer = combineReducers({
  bookResults: bookResultsReducer
});
export default rootReducer;
The reducer
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');
  }
}
