React : How to stop re-rendering component or making an API calls on router change for the same route

回眸只為那壹抹淺笑 提交于 2020-05-15 18:10:57

问题


I need help to stop making same API calls again on route change. I tried to find the solution available on the web and on stackoverflow, but I am not able to figure out what changes require to fix this thing.

I also tried to use sessionStorage to store one flag that if API calls fired then flag set to true and on router change I check if flag is true then don't make any API calls. But then it blanks the list of articles.

I have one main App component with routes, which contains two components : ( Please see below given code )

  1. ArticleHome
  2. BookmarkHome

the problem which I am facing is, when I first time hits http://localhost:3001, it redirects to http://localhost:3001/articles page and it renders ArticleHome component properly and fetches list of Articles. then I click on Bookmarks link and it renders http://localhost:3001/bookmarks route and related component and makes and API call to render bookmark list.

But when I go back to Articles page using router http://localhost:3001/articles it again fires API call to fetch articles due to component re-renders on each route change.

Can anyone pleae guide that how can I stop executing API calls again on router change and on component re-render?

Is there any better way to handle API calls on router change for the same component, if data is already loaded?

class App extends React.Component<any, any> {

    constructor(props: any) {
        super(props);
    }

    render() {
        return (
            <Router>
                <React.Fragment>
                    <Switch>
                        <Route exact path="/" render={() => <Redirect to="/articles" />} />
                        <Route path="/articles" render={() => <ArticleHome type="articles" />} />
                        <Route path="/bookmarks" render={() => <BookmarkHome type="bookmarks" />} />
                        <Route path="*" component={NoMatch} />
                    </Switch>
                </React.Fragment>
            </Router>
        );
    }
}

export default App;

In ArticleHome component I have one API call which fetches list of Articles and in BookmarkHome component I have another API call which fetches list of Bookmarks.

ArticleHome component: ( Same function for BookmarkHome component )

import React, { useReducer, useEffect, useState } from "react";

const ArticleHome = (props: any) => {

    const [state, setState] = useState({
        articles: [],
        reRender: true
    });

    const [newState, dispatch] = useReducer(articleReducer, state);

    useEffect(() => {


        // componentWillUnmount
        return () => {

            console.log('unmounting 1...');
            setState({
                ...state,
                reRender: false
            });
        }

    },[state.reRender]); // componentDidUpdate

    const fetchAllrecords = () => {
        fetch(url)
        .then((data: any) => {

            dispatch({ type: 'GET_ALL_RECORDS', data: data.docs })

            return data.docs;
        })
    }

return (
    <ul>
        <li>Render article list here</li>
    </ul>
)
}


// Reducer function
// ========================

function articleReducer(state: any, action: any) {
    switch (action.type) {
        case "GET_ALL_RECORDS":
            return {
                ...state,
                articles: action.data,
                reRender: false
            }
        }
        default:
            return state;
    }
}

But when I switch to any of the above mentioned components using router it again executes API call.

I have passed [state.reRender] in useEffect hook to tell react that re-render component only when any new items added or data updates.

In articleReducer function I am setting reRender: false.

Please let me know if you need further information on this.

Thanks, Jignesh Raval


回答1:


If I were you I would fetch from my api outside of the route components and just pass it down to the route. Then you don't have to worry about the route change and any programming that would be involved in manipulating the default behavior of the router. You can do this one of two ways.

First Do your api call higher in the component tree. So if you would do your api call in the app component then pass it down to the Route you wouldn't have to worry about the rendering of the route. Your logic would be outside of it.

Second If you don't want a messy app component you could create a context component and do your logic in there. Then just wrap your components in your context component that you created. Like so:

Here is a codesandbox Sandbox

Your app component

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Route, Link, Switch } from "react-router-dom";

import First from "./First";
import Second from "./Second";
import Context from "./Context";
import "./styles.css";

function App() {
  return (
    <BrowserRouter>
      <div className="App">
        <h1>Router Rerendering Fix</h1>
        <ul>
          <li>
            <Link to="/">First Route</Link>
          </li>
          <li>
            <Link to="/second">Second Route</Link>
          </li>
        </ul>
        <Switch>
          <Context>
            <Route exact path="/" component={First} />
            <Route path="/second" component={Second} />
          </Context>
        </Switch>
      </div>
    </BrowserRouter>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Your articles component

import React, { useContext } from "react";
import {ApiContext} from "./Context";

const First = () => {
  const context = useContext(ApiContext);
  return (
    <div>
      <div>First Route</div>
      <ul>
        {context.map((article, index) => (
          <li key={index}>{article}</li>
        ))}
      </ul>
    </div>
  );
};

export default First;

Your context component

import React, {useState, useEffect} from 'react';

export const ApiContext = React.createContext();

const Context = props => {
  const [articles, setArticles] = useState([]);

  useEffect(() => {
    console.log('rendered');
    setArticles(['Article One', 'Article Two', '...ect'])
  }, []);

  return (
    <ApiContext.Provider value={articles}>
      {props.children}
    </ApiContext.Provider>
  )
}

export default Context;


来源:https://stackoverflow.com/questions/57376614/react-how-to-stop-re-rendering-component-or-making-an-api-calls-on-router-chan

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!