用新数据重新渲染组件,而无需在React中保持本地状态

时间:2018-10-03 12:42:13

标签: javascript reactjs redux react-router redux-thunk

我正在练习react和redux,并且正在创建一个简单的应用,其中的侧边栏显示了在每条路线上可见的类别列表,并且其主区域最初显示了我拥有的所有书籍以及在单击时侧边栏上的类别链接,主要区域加载了另一个组件以及与此类别相关的所有书籍。

这是我在App.js文件中设置的路线...

class App extends Component {
    async componentDidMount() {
        try {
            await this.props.asyncLoadBooks();
            await this.props.asyncLoadCategories();
        } catch (error) {
            console.log(error);
        }
    }

    render() {
        return (
            <>
                <Header />
                <div className="global-wrapper">
                    <div className="container">
                        <aside className="side-bar">
                            <Categories />
                        </aside>

                        <main className="main-content">
                            <Switch>
                                <Route exact path="/" component={Books} />
                                <Route
                                    exact
                                    path="/category/:id"
                                    component={Category}
                                />
                                <Route component={NotFound} />
                            </Switch>
                        </main>
                    </div>
                </div>
            </>
        );
    }
}

App.js中,您可以看到ActionsbooksActions的{​​{1}}文件中的axios正在通过本地JSON文件通过axios加载数据,它是非常简单。

这是“类别”组件...

categoriesAction

我要在单个Category组件的class Categories extends Component { render() { const { categories } = this.props; let categoriesList; if (categories && categories.length !== 0) { categoriesList = categories.map(category => ( <li key={category.id}> <Link to={`/category/${category.id}`}>{category.name}</Link> </li> )); } else { categoriesList = <Loading />; } return ( <div> <h2>Categories</h2> <ul>{categoriesList}</ul> </div> ); } } const mapState = state => ({ categories: state.categories.categories }); export default connect(mapState)(Categories); 中执行另一项操作,以获取与该组件相关的所有书籍并呈现它们...

ComponentDidMount()

现在,一切都在第一次运行,但是,当我单击另一个类别时,class Category extends Component { componentDidMount() { this.props.getCategoryBooks(this.props.match.params.id); } componentDidUpdate(prevProps) { if (prevProps.match.params.id !== this.props.match.params.id) { this.props.getCategoryBooks(this.props.match.params.id); } } render() { const { categoryBooks } = this.props; return ( <div> {/* <h1>{this.props.match.params.id}</h1> */} {categoryBooks && categoryBooks.map(book => { return <div key={book.id}>{book.title}</div>; })} </div> ); } } const mapState = state => ({ categories: state.categories.categories, categoryBooks: state.books.categoryBooks }); const mapActions = { getCategoryBooks }; export default connect( mapState, mapActions )(Category); 组件不会得到更新,因为我正在<Category />中分派操作,因此该组件已经第一次安装,因此在我单击另一个类别后它不会再次调度该动作,现在最好的处理方法是什么?

第二个问题是我在类别路由componentDidMount()上,我尝试刷新页面,categoris列表在侧边栏上加载正常,但是单个组件显示为空,当我查看redux-devtools我发现http://localhost:3000/category/9967c77a-1da5-4d69-b6a9-014ca20abd61文件中的GET_CATEGORY_BOOKSLOAD_BOOKS之前触发了LOAD_CATEGORIES操作,因为子App.js方法在它的子方法之前被调用父等效方法。该如何解决呢?

我希望你们能在这方面帮助我。

编辑

正如@@NguyễnThanhTú注意到的那样,componentDidupate有一个错字,现在可以在单击另一个类别时使用。 当在类别路由中重新加载页面时,这给我们带来了第二个问题,并且由于App.js componentDidMount在其子组件之后触发而无法显示数据。

编辑

以下是此项目的Github回购... https://github.com/Shaker-Hamdi/books-app

1 个答案:

答案 0 :(得分:1)

在您的booksActions.js中,添加以下内容:

export const getCategoryBooksV2 = categoryId => {
    return async (dispatch, getState) => {
        const { books } = getState();
        if (books.books.length === 0) {
            console.log('Only executing once')  // testing purpose only
            const response = await axios.get("books.json");
            const data = response.data.books;
            dispatch(loadBooks(data));
            dispatch(getCategoryBooks(categoryId));
        }
        dispatch(getCategoryBooks(categoryId));
    };
};

在您的Category.js中,使用该新动作创建者:

import { getCategoryBooksV2 } from "../books/booksActions";
...
componentDidMount() {
    this.props.getCategoryBooksV2(this.props.match.params.id);
}
...
const mapActions = {
    getCategoryBooksV2
};

此解决方案受此示例启发:

function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();

    if (counter % 2 === 0) {
      return;
    }

    dispatch(increment());
  };
}

Redux-Thunk Documentation

这是演示:

enter image description here