从子组件返回时,反应状态将被覆盖

时间:2019-07-21 08:07:01

标签: reactjs redux react-router redux-thunk

我有一个主页,该页面从GET调用(使用axios)加载书卡(带有书本图像,书名等),我正在使用redux操作调用程序来触发API调用“ getAllBooks”在componentDidMount()上。我还使用react-router Link组件将书与“ id”链接,一旦用户单击书证,他将被带到BookView页面,该ID取自this.props.match.params。 BookView组件使用另一个操作调用程序(位于上一个操作调用程序的同一文件中,并且共享相同的reducer)加载API调用以获取“ bookById”。

当我从BookView页面返回主页时(使用浏览器的后退按钮或使用this.props.history.goBack()),会出现问题。返回到HomeView时,状态将被“ BookByID”操作覆盖,并且我无法恢复到旧状态(因此,在主页上出现undefined错误)。 我尝试将操作放入不同的文件中(当然这是没有用的,因为我对两个操作都使用了相同的reducer)。我尝试在首页上设置componentDidUpdate来在道具不匹配时触发动作。尝试将动作分派到(该reduce的)redux状态以将其重置,但没有任何效果。我不确定自己犯了什么错误,请足够友好地指导我解决问题。 为此,我正在使用React 16.8和Redux 7.1(以及Thunk来帮助异步调用)。

Home.js


import { getBooks, resetGetBooks } from "../../redux/actions/bookDbAction";
class Home extends Component {
  constructor(props) {
    super(props);

    this.signal = true;
    this.titleInput = React.createRef();
    this.descriptionInput = React.createRef();
    this.isbnInput = React.createRef();
    this.isbn13Input = React.createRef();
    this.grIdInput = React.createRef();
    this.imgLinkLargeInput = React.createRef();
    this.imgLinkMediumInput = React.createRef();
    this.imgLinkSmallInput = React.createRef();
  }

  componentDidMount() {
    this.props.getBooks();
  }

  // when component re-renders
  // componentDidUpdate(prevProps, prevState) {
  //   if(prevProps.books !== this.props.books) {
  //     this.props.getBooks();
  //   }
  // }

  renderBooks() {
    const { classes, books, loading, error } = this.props;
    if (loading) {
      return (
        <div className={classes.progressWrapper}>
          <CircularProgress />
        </div>
      );
    }

    if (books.length === 0) {
      return (
        <Typography className={classes.noResults} variant="h4">
          There are no books available
        </Typography>
      );
    }

    return (
      <Grid container spacing={3}>
        {books.map(book => (
          <Grid item key={book.id} lg={2} md={4} xs={6}>
            <Link className={classes.link} to={`/book/${book.id}`}>
              <BookCardGrid book={book} />
            </Link>
          </Grid>
        ))}
      </Grid>
    );
  }

  render() {
    const { classes } = this.props;

    return (
      <CoreLayout title="Home">
        <div className={classes.root}>
          <BookToolbar />
          <div className={classes.content}>{this.renderBooks()}</div>
          <div className={classes.pagination}>
            <Typography variant="caption">1-6 of 20</Typography>
            <IconButton>
              <ChevronLeftIcon />
            </IconButton>
            <IconButton>
              <ChevronRightIcon />
            </IconButton>
          </div>
        </div>
      </CoreLayout>
    );
  }
}

const mapStateToProps = state => {
  return {
    books: state.book.data,
    loading: state.book.dataLoading,
    error: state.book.error
  };
};

const mapDispatchToProps = {
  getBooks,
  resetGetBooks
};

Home.defaultProps = {
  books: [],
  loading: true,
  error: ""
};

Home.propTypes = {
  classes: PropTypes.object.isRequired,
  books: PropTypes.array.isRequired,
  loading: PropTypes.bool.isRequired,
  error: PropTypes.string.isRequired
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withStyles(styles)(Home));

Book.js <---这是BookView页面

import { getBookById, resetGetBookById } from "../../redux/actions/bookDbAction";


class Book extends Component {
  constructor(props) {
    super(props);

    this.signal = true;

    this.state = {
      isLoading: false,
      book: {
        bookTitle: "",
        description: "",
        isbn: "",
        isbn13: "",
        grId: "",
        imgLinkLarge: "",
        imgLinkMedium: "",
        imgLinkSmall: ""
      },
      error: null
    };
  }

  componentDidMount() {
    const { id } = this.props.match.params;
    this.props.getBookById(id);
  }

  componentWillUnmount() {
    this.props.resetGetBookById();
  }

  goBack = () => {
    this.props.history.goBack();
  };

  render() {
    const { classes, book, loading, error } = this.props;

    return (
      <CoreLayout title={book.title}>
        <div className={classes.root}>
          <IconButton
            className={classes.iconButton}
            onClick={this.goBack}
            size="medium"
          >
            <BackIcon fontSize="medium" />
          </IconButton>
          {loading ? (
            <div className={classes.progressWrapper}>
              <CircularProgress />
            </div>
          ) : (
            <div className={classes.content}>
              <div className={classes.imageWrapper + " image-wrap"}>
                <img
                  alt={book.title}
                  className={classes.image}
                  src={book.img_m}
                />
              </div>
            </div>
          )}
        </div>
      </CoreLayout>
    );
  }
}

Book.propTypes = {
  classes: PropTypes.object.isRequired
};

Book.defaultProps = {
  books: [],
  loading: true,
  error: ""
};

const mapStateToProps = state => {
  return {
    book: state.book.data,
    loading: state.book.dataLoading,
    error: state.book.error
  };
};

const mapDispatchToProps = {
  getBookById,
  resetGetBookById
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(withStyles(styles)(Book));

BookDbAction.js <---这是具有所有动作创建者的Book动作文件

import {
  GET_ALL_BOOKS_PENDING,
  GET_ALL_BOOKS_SUCCESS,
  GET_ALL_BOOKS_FAILURE,
  GET_ALL_BOOKS_RESET,
  GET_BOOK_BY_ID_PENDING,
  GET_BOOK_BY_ID_SUCCESS,
  GET_BOOK_BY_ID_FAILURE,
  GET_BOOK_BY_ID_RESET
} from "./types";

import axios from "axios";

const URL = `${process.env.REACT_APP_DEVELOPMENT_SERVER_URL}/book`;

export const getBooksPending = () => ({
  type: GET_ALL_BOOKS_PENDING,
  dataLoading: true
});

export const getBooksSuccess = json => ({
  type: GET_ALL_BOOKS_SUCCESS,
  dataLoading: false,
  payload: json
});

export const getBooksFailure = error => ({
  type: GET_ALL_BOOKS_FAILURE,
  dataLoading: false,
  payload: error
});

export const getBooksReset = () => ({
  type: GET_ALL_BOOKS_RESET
});

export const getBooks = () => {
  return async dispatch => {
    try {
      let response = await axios.get(URL);
      dispatch(getBooksPending());
      let data = await response.data;
      dispatch(getBooksSuccess(data));
    } catch (error) {
      console.log(error);
      dispatch(getBooksFailure(error));
    }
  };
};

export const resetGetBooks = () => {
  return dispatch => {
    dispatch(getBooksReset());
  };
};

export const getBookByIdPending = () => ({
  type: GET_BOOK_BY_ID_PENDING,
  dataLoading: true
});

export const getBookByIdSuccess = json => ({
  type: GET_BOOK_BY_ID_SUCCESS,
  dataLoading: false,
  payload: json
});

export const getBookByIdFailure = error => ({
  type: GET_BOOK_BY_ID_FAILURE,
  dataLoading: false,
  payload: error
});

export const getBookByIdReset = () => ({
  type: GET_BOOK_BY_ID_RESET
});

export const getBookById = id => {
  return async dispatch => {
    try {
      let response = await axios.get(`${URL}/${id}`);
      dispatch(getBookByIdPending());
      let json = await response.data;
      dispatch(getBookByIdSuccess(json));
    } catch (error) {
      dispatch(getBookByIdFailure(error));
    }
  };
};

export const resetGetBookById = () => {
  return dispatch => {
    dispatch(getBookByIdReset());
  };
};

export const addBookPending = () => ({
  type: ADD_BOOK_PENDING,
  dataLoading: true
});

export const addBookSuccess = data => ({
  type: ADD_BOOK_SUCCESS,
  dataLoading: false,
  payload: data
});

export const addBookFailure = error => ({
  type: ADD_BOOK_FAILURE,
  dataLoading: false,
  payload: error
});

export const addBookReset = () => ({
  type: ADD_BOOK_RESET
});

export const addBook = data => {
  return async dispatch => {
    try {
      let response = await axios.post(`${URL}`, {
        grid: data.grId,
        title: data.bookTitle,
        descr: data.description,
        isbn: data.isbn,
        isbn13: data.isbn13,
        img_l: data.imgLinkLarge,
        img_m: data.imgLinkMedium,
        img_s: data.imgLinkSmall
      });
      dispatch(addBookPending());
      let id = await response.data;
      const data = { id, ...data };
      // console.log("res:" + JSON.stringify(response));
      // console.log("id:" + id);
      dispatch(addBookSuccess(data));
    } catch (error) {
      dispatch(addBookFailure(error));
    }
  };
};

export const resetaddBook = () => {
  return dispatch => {
    dispatch(addBookReset());
  };
};

b> BookDbReducer.js <---这是Book reducer文件

import {
  GET_ALL_BOOKS_PENDING,
  GET_ALL_BOOKS_SUCCESS,
  GET_ALL_BOOKS_FAILURE,
  GET_BOOK_BY_ID_PENDING,
  GET_BOOK_BY_ID_SUCCESS,
  GET_BOOK_BY_ID_FAILURE,
  GET_BOOK_BY_ID_RESET
} from "../actions/types";

const initialState = {
  dataLoading: true,
  data: [],
  error: ""
};

const bookDbReducer = (state = initialState, action) => {
  switch (action.type) {
    case GET_ALL_BOOKS_PENDING:
      return {
        ...state,
        dataLoading: action.dataLoading
      };
    case GET_ALL_BOOKS_SUCCESS:
      return {
        ...state,
        dataLoading: action.dataLoading,
        data: action.payload
      };
    case GET_ALL_BOOKS_FAILURE:
      return {
        ...state,
        dataLoading: action.dataLoading,
        error: action.payload
      };
    case GET_BOOK_BY_ID_PENDING:
      return {
        ...state,
        dataLoading: action.dataLoading
      };
    case GET_BOOK_BY_ID_SUCCESS:
      return {
        ...state,
        dataLoading: action.dataLoading,
        data: action.payload
      };
    case GET_BOOK_BY_ID_FAILURE:
      return {
        ...state,
        dataLoading: action.dataLoading,
        error: action.payload
      };
    case GET_BOOK_BY_ID_RESET:
      return {
        ...state,
        dataLoading: false,
        data: null,
        error: null
      };
    default:
      return state;
  }
};

export default bookDbReducer;

1 个答案:

答案 0 :(得分:0)

您应该尝试将书籍详细信息数据存储在另一个商店属性中。这样一来,将保留一系列书籍。

const initialState = {
  dataLoading: true,
  data: [],
  bookDetails: {},
  error: ""
};


case GET_BOOK_BY_ID_SUCCESS:
  return {
    ...state,
    dataLoading: action.dataLoading,
    bookDetails: action.payload
  };