[英]How to properly update React Context state and avoid re-rendering?
I'm currently learning React Context API and I've a project with following files (I'm using create-create-app to generate the project):我目前正在学习 React Context API 并且我有一个包含以下文件的项目(我正在使用create-create-app生成项目):
Tree树
├── package.json
├── node_modules
│ └── ...
├── public
│ └── ...
├── src
│ ├── components
│ │ ├── App.js
│ │ ├── Container.js
│ │ ├── Info.js
│ │ ├── PageHeading.js
│ │ ├── common
│ │ │ ├── Footer.js
│ │ │ ├── Header.js
│ │ │ ├── Helpers.js
│ │ │ ├── Loading.css
│ │ │ ├── Loading.js
│ │ │ └── SearchForm.js
│ │ └── list
│ │ ├── Pagination.js
│ │ └── Table.js
│ ├── css
│ │ └── ...
│ ├── fonts
│ │ └── ...
│ ├── images
│ │ └── ...
│ ├── index.css
│ ├── index.js
│ └── providers
│ └── GlobalProvider.js
└── yarn.lock
File: src/index.js文件:src/index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
import Header from "./components/common/Header";
import Footer from "./components/common/Footer";
ReactDOM.render(
[<Header key='1' />, <App key='2' />, <Footer key='3' />],
document.getElementById("root")
);
File: src/providers/GlobalProvider.js文件:src/providers/GlobalProvider.js
import React, { Component } from "react";
import { handleResponse } from "../components/common/Helpers";
// Set up initial context
export const GlobalContext = React.createContext({});
// Create an exportable consumer that can be injected into components
export const GlobalConsumer = GlobalContext.Consumer;
// Create the provider using a traditional React.Component class
class GlobalProvider extends Component {
state = {
age: 100,
error: null,
isLoading: false,
currentPage: 1,
result: "",
data: null,
total_hg_customers: "",
total_grams_by_customers: "",
totalPages: "",
hasNextPage: false,
hasPrevPage: false
};
fetchCustomersByPage = () => {
this.setState({ isLoading: true });
const { currentPage } = this.state;
fetch(
`https://api-server.com/customer_gold?pageNum=${currentPage}`
)
.then(handleResponse)
.then(response => {
const {
currentPage,
result,
data,
total_hg_customers,
total_grams_by_customers,
totalPages,
hasNextPage,
hasPrevPage
} = response;
this.setState({
isLoading: false,
currentPage,
result,
data,
total_hg_customers,
total_grams_by_customers,
totalPages,
hasNextPage,
hasPrevPage
});
console.log("API request done and state has been updated"); // <-- this one get infinitely generated
})
.catch(error => {
this.setState({
error: error.errorMessage,
loading: false
});
});
};
render() {
return (
// value prop is where we define what values
// that are accessible to consumer components
<GlobalContext.Provider
value={{
state: this.state,
fetchCustomersByPage: this.fetchCustomersByPage
}}>
{this.props.children}
</GlobalContext.Provider>
);
}
}
export default GlobalProvider;
File: src/components/common/Helpers.js文件:src/components/common/Helpers.js
export const handleResponse = response => {
return response.json().then(json => {
return response.ok ? json : Promise.reject(json);
});
};
File: src/components/App.js文件:src/components/App.js
import React, { Component } from "react";
import GlobalProvider from "../providers/GlobalProvider";
import PageHeading from "./PageHeading";
import Container from "./Container";
class App extends Component {
render() {
return (
<GlobalProvider>
<div className='root-container content'>
<PageHeading />
<Container />
</div>
</GlobalProvider>
);
}
}
export default App;
File: src/components/Container.js文件:src/components/Container.js
import React, { Component } from "react";
import Loading from "./common/Loading";
import Info from "./Info";
import SearchForm from "./common/SearchForm";
import Table from "./list/Table";
import Pagination from "./list/Pagination";
import { GlobalConsumer } from "../providers/GlobalProvider";
class Container extends Component {
render() {
return (
<GlobalConsumer>
{context => {
const { isLoading } = context.state;
if (isLoading) {
return (
<div className='loading-container'>
<Loading />
</div>
);
}
return (
<div className='middle-container'>
<div className='container'>
<div className='success-container'>
<div className='row'>
<Info />
<SearchForm />
</div>
<div className='row'>
<Table />
</div>
<div className='row pagination'>
<Pagination />
</div>
</div>
</div>
</div>
);
}}
</GlobalConsumer>
);
}
}
export default Container;
File: src/components/list/Table.js文件:src/components/list/Table.js
import React, { Component } from "react";
import { GlobalConsumer, GlobalContext } from "../../providers/GlobalProvider";
class Table extends Component {
static contextType = GlobalContext;
componentDidMount() {
console.log("Hello");
return this.context.fetchCustomersByPage();
}
render() {
return (
<div className='col-12'>
<GlobalConsumer>
{context => {
const { data } = context.state;
return (
<table className='customer-list'>
<thead>
<tr>
<th width='50%'>Account Number</th>
<th width='50%'>Gold Balance (g)</th>
</tr>
</thead>
<tbody>
{data &&
data.map(customer => (
<tr key={customer.account_number}>
<td>{customer.account_number}</td>
<td>{customer.gold_balance}</td>
</tr>
))}
</tbody>
</table>
);
}}
</GlobalConsumer>
</div>
);
}
}
export default Table;
The problem right now is it continue making API calls to the API server (infinite call after one and another).现在的问题是它继续对 API 服务器进行 API 调用(一个接一个的无限调用)。 When I remove
this.setState({isLoading: false, currentPage, ...})
part, it stopped making the infinite call to the API server.当我删除
this.setState({isLoading: false, currentPage, ...})
部分时,它停止了对 API 服务器的无限调用。 But of course, the app's isLoading
state stucked at true
and the loading spinner component persists in the view.但当然,应用程序的
isLoading
state 卡在true
并且加载微调器组件仍然存在于视图中。
All I need is just submit one call to the API server → assign the respond to the provider/global state → remove the loading spinner → render the table view with the data from the API call.我只需要向 API 服务器提交一个调用 → 将响应分配给提供者/全局 state → 移除加载微调器 → 使用来自 ZDB974238714CA8DE634A7CE1D083A14F 调用的数据呈现表格视图。 I tried using why-did-you-update package to find out what was wrong but I didn't see any clue.
我尝试使用Why-did-you-update package 来找出问题所在,但我没有看到任何线索。
What I did wrong here?我在这里做错了什么? Please point it out to me.
请向我指出。 If I missed pasting any important files here, please let me know.
如果我错过了在这里粘贴任何重要文件,请告诉我。 Thank you.
谢谢你。
Is it possible use React Hook?是否可以使用 React Hook? Based on your code, I recommend you break it down into React Hook.
根据您的代码,我建议您将其分解为 React Hook。 If so you can use useCallback or useMemo hook.
如果是这样,您可以使用 useCallback 或 useMemo 挂钩。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.