在 React 中没有本地状态的情况下使用新数据重新渲染组件

Posted

技术标签:

【中文标题】在 React 中没有本地状态的情况下使用新数据重新渲染组件【英文标题】:Re-render the component with new data without having local state in React 【发布时间】:2019-03-08 16:47:08 【问题描述】:

我正在练习 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 中,您可以看到我通过本地JSON 文件加载数据,其中booksActionsbooksActionscategoriesActionActions 文件中带有axios,这非常简单。

这里是类别组件...

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);

我正在单个 Category 组件的 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);

现在,第一次一切正常,但是,当我单击另一个类别时,&lt;Category /&gt; 组件没有更新,因为我在 componentDidMount() 中调度操作,因此该组件已在第一次安装,因此在我单击另一个类别后它不会再次调度操作,现在处理这个问题的最佳方法是什么?

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

我希望你们能帮助我。

编辑

正如@@NguyễnThanhTú 注意到的,componentDidupate 有一个错字,现在点击另一个类别时它可以工作。 这给我们留下了第二个问题,在类别路由中重新加载页面时,由于 App.js componentDidMount 在其子组件之后触发,因此数据不显示。

编辑

这是这个项目在 Github 上的一个 repo ... https://github.com/Shaker-Hamdi/books-app

【问题讨论】:

@NguyễnThanhTú 你是对的,这是一个错字,现在每次我点击一个类别时它都会工作,组件确实会更新新的书籍列表,谢谢。现在我在那里做的事情是正确的吗?调用动作两次?一次在componentDidMount 和一次componentDidUpate?我只是问问。这也给我们留下了第二个问题,当刷新页面时,子 componentDidMount 在其父之前出现。 您能否在 GitHub 上提供这个简单的应用程序?我会尝试修复它,并在完成后通知您 @NguyễnThanhTú 给你……github.com/Shaker-Hamdi/books-app 您的问题中有太多不相关的代码和信息。此外,您应该按照 SO 论坛指南直接在您的答案中发布所有相关代码。下面的答案似乎基于您上面评论中的 github 链接中的代码。 @twils0,伙计,不仅仅是因为您没有得到问题,而是将其丢弃。冷静点。 【参考方案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

这是演示:

【讨论】:

以上是关于在 React 中没有本地状态的情况下使用新数据重新渲染组件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React 中动态创建状态?

浏览器刷新后如何保持 React 组件状态

在不使用 React.createClass 的情况下使用 props 在 react 中播种初始状态

如何在不使用 React Native 中的堆栈导航器的情况下重定向到页面?

重定向后反应状态数据未准备好

如何在不推送到服务器的情况下更新中继存储