从 child 组件返回时反应状态被覆盖

React state is getting overwritten when coming back from child component

我有一个从 GET 调用(使用 axios)加载图书卡片(带有图书图片、标题等)的主页,我正在使用 redux 操作调用程序来触发 API 在 componentDidMount() 上调用 "getAllBooks"。我还使用 react-router Link 组件来 link 带有 "id" 的书,一旦用户点击书卡,他将被带到 BookView 页面其中 id 取自 this.props.match.params。 BookView 组件加载 API 调用以获取 "bookById",使用另一个动作调用者(它与前一个动作调用者在同一个文件中,并且都共享同一个 reducer)。

当我从 BookView 页面返回主页时出现问题(使用浏览器的后退按钮或使用 this.props.history.goBack())。当返回到 HomeView 时,状态被 "BookByID" 操作覆盖,我无法恢复旧状态(因此我在主页上收到 undefined 错误)。 我尝试将动作放在不同的文件中(当然这是无用的,因为我对这两个动作使用相同的 reducer)。我尝试在主页上设置 componentDidUpdate 以在道具不匹配时触发操作。尝试将操作分派到(该减速器的)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 reducerfile

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;

您应该尝试将图书详细信息数据存储在另一个商店中 属性。这样书籍数组就会被保留下来。

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


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