删除 duplicate/redundant API 请求的最佳做法是什么?
What is best practice to remove duplicate/redundant API requests?
所以我在一个 React 平台上工作,该平台的数据每秒更新一次(我想转移到网络套接字,但它目前只支持获取)。目前,每个组件都会为自己发出获取请求以获取小部件的数据。由于提取请求内置于小部件中,因此存在对相同数据的冗余 api 请求。我正在寻找一个可能更好的解决方案来删除这些冗余 api 请求。
我想出的解决方案使用了我称之为数据服务的东西,该服务检查对数据源的任何订阅,然后进行这些 api 调用,并将数据置于 redux 状态,供组件使用.我不确定这是否是处理我试图避免的问题的最佳方式。我不喜欢我需要一个间隔 运行 应用程序每秒 运行 检查是否有“订阅”。我不确定这是否是正确的方法。使用此解决方案,我不会重复任何请求,并且可以在不影响其他组件的情况下添加或删除订阅。
还有一件事,id 可以改变并且会改变我收到的数据
这是我如何处理服务的简化版本。
const reduxState = {
id: "specific-id",
subscriptions: {
sourceOne: ["source-1-id-1", "source-1-id-2", "source-1-id-3"],
sourceTwo: ["source-2-id-1", "source-one-id-2"],
},
data: {
sourceOne: { id: "specific-id", time: "milliseconds", data: "apidata" },
sourceTwo: { id: "specific-id", time: "milliseconds", data: "apidata" },
},
};
const getState = () => reduxState; //Would be a dispatch to always get current redux state
const dataService = () => {
const interval = setInterval(() => {
const state = getState();
if (state.subscriptions.sourceOne.length > 0)
fetchSourcOneAndStoreInRedux();
if (state.subscriptions.sourceTwo.length > 0)
fetchSourceTwoAndStoreInRedux();
}, 1000);
};
const fetchSourcOneAndStoreInRedux = (id) =>{
return async dispatch => {
try {
const res = await axios.get(`/data/one/${id}`)
dispatch(setSourceOneDataRedux(res.data))
} catch (err) {
console.error(err)
}
}
}
我正在构建我的组件以仅显示来自正确 ID 的数据。
这是一个简单的“DataManager”的简单工作示例,它可以实现您正在寻找的功能。
class DataManager {
constructor(config = {}) {
this.config = config;
console.log(`DataManager: Endpoint "${this.config.endpoint}" initialized.`);
if (this.config.autostart) { // Autostart the manager if autostart property is true
this.start();
}
}
config; // The config object passed to the constructor when initialized
fetchInterval; // The reference to the interval function that fetches the data
data; // Make sure you make this state object observable via MOBX, Redux etc so your component will re-render when data changes.
fetching = false; // Boolean indicating if the APIManager is in the process of fetching data (prevent overlapping requests if response is slow from server)
// Can be used to update the frequency the data is being fetched after the class has been instantiated
// If interval already has been started, stop it and update it with the new interval frequency and start the interval again
updateInterval = (ms) => {
if (this.fetchInterval) {
this.stop();
console.log(`DataManager: Updating interval to ${ms} for endpoint ${this.config.endpoint}.`);
this.config.interval = ms;
this.start();
} else {
this.config.interval = ms;
}
return this;
}
// Start the interval function that polls the endpoint
start = () => {
if (this.fetchInterval) {
clearInterval(this.fetchInterval);
console.log(`DataManager: Already running! Clearing interval so it can be restarted.`);
}
this.fetchInterval = setInterval(async () => {
if (!this.fetching) {
console.log(`DataManager: Fetching data for endpoint "${this.config.endpoint}".`);
this.fetching = true;
// const res = await axios.get(this.config.endpoint);
// Commented out for demo purposes but you would uncomment this and clear the anonymous function below
const res = {};
(() => {
res.data = {
dataProp1: 1234,
dataProp2: 4567
}
})();
this.fetching = false;
this.data = res.data;
} else {
console.log(`DataManager: Waiting for pending response for endpoint "${this.config.endpoint}".`);
}
}, this.config.interval);
return this;
}
// Stop the interval function that polls the endpoint
stop = () => {
if (this.fetchInterval) {
clearInterval(this.fetchInterval);
console.log(`DataManager: Endpoint "${this.config.endpoint}" stopped.`);
} else {
console.log(`DataManager: Nothing to stop for endpoint "${this.config.endpoint}".`);
}
return this;
}
}
const SharedComponentState = {
source1: new DataManager({
interval: 1000,
endpoint: `/data/one/someId`,
autostart: true
}),
source2: new DataManager({
interval: 5000,
endpoint: `/data/two/someId`,
autostart: true
}),
source3: new DataManager({
interval: 10000,
endpoint: `/data/three/someId`,
autostart: true
})
};
setTimeout(() => { // For Demo Purposes, Stopping and starting DataManager.
SharedComponentState.source1.stop();
SharedComponentState.source1.updateInterval(2000);
SharedComponentState.source1.start();
}, 10000);
// Heres what it would look like to access the DataManager data (fetched from the api)
// You will need to make sure you pass the SharedComponentState object as a prop to the components or use another React mechanism for making that SharedComponentState accessible to the components in your app
// Accessing state for source 1: SharedComponentState.source1.data
// Accessing state for source 2: SharedComponentState.source2.data
// Accessing state for source 3: SharedComponentState.source3.data
基本上,DataManager class 的每个实例都负责获取不同的 api 端点。我包括了一些其他 class 方法,允许您启动、停止和更新 DataManager 实例的轮询频率。
所以我在一个 React 平台上工作,该平台的数据每秒更新一次(我想转移到网络套接字,但它目前只支持获取)。目前,每个组件都会为自己发出获取请求以获取小部件的数据。由于提取请求内置于小部件中,因此存在对相同数据的冗余 api 请求。我正在寻找一个可能更好的解决方案来删除这些冗余 api 请求。
我想出的解决方案使用了我称之为数据服务的东西,该服务检查对数据源的任何订阅,然后进行这些 api 调用,并将数据置于 redux 状态,供组件使用.我不确定这是否是处理我试图避免的问题的最佳方式。我不喜欢我需要一个间隔 运行 应用程序每秒 运行 检查是否有“订阅”。我不确定这是否是正确的方法。使用此解决方案,我不会重复任何请求,并且可以在不影响其他组件的情况下添加或删除订阅。
还有一件事,id 可以改变并且会改变我收到的数据
这是我如何处理服务的简化版本。
const reduxState = {
id: "specific-id",
subscriptions: {
sourceOne: ["source-1-id-1", "source-1-id-2", "source-1-id-3"],
sourceTwo: ["source-2-id-1", "source-one-id-2"],
},
data: {
sourceOne: { id: "specific-id", time: "milliseconds", data: "apidata" },
sourceTwo: { id: "specific-id", time: "milliseconds", data: "apidata" },
},
};
const getState = () => reduxState; //Would be a dispatch to always get current redux state
const dataService = () => {
const interval = setInterval(() => {
const state = getState();
if (state.subscriptions.sourceOne.length > 0)
fetchSourcOneAndStoreInRedux();
if (state.subscriptions.sourceTwo.length > 0)
fetchSourceTwoAndStoreInRedux();
}, 1000);
};
const fetchSourcOneAndStoreInRedux = (id) =>{
return async dispatch => {
try {
const res = await axios.get(`/data/one/${id}`)
dispatch(setSourceOneDataRedux(res.data))
} catch (err) {
console.error(err)
}
}
}
我正在构建我的组件以仅显示来自正确 ID 的数据。
这是一个简单的“DataManager”的简单工作示例,它可以实现您正在寻找的功能。
class DataManager {
constructor(config = {}) {
this.config = config;
console.log(`DataManager: Endpoint "${this.config.endpoint}" initialized.`);
if (this.config.autostart) { // Autostart the manager if autostart property is true
this.start();
}
}
config; // The config object passed to the constructor when initialized
fetchInterval; // The reference to the interval function that fetches the data
data; // Make sure you make this state object observable via MOBX, Redux etc so your component will re-render when data changes.
fetching = false; // Boolean indicating if the APIManager is in the process of fetching data (prevent overlapping requests if response is slow from server)
// Can be used to update the frequency the data is being fetched after the class has been instantiated
// If interval already has been started, stop it and update it with the new interval frequency and start the interval again
updateInterval = (ms) => {
if (this.fetchInterval) {
this.stop();
console.log(`DataManager: Updating interval to ${ms} for endpoint ${this.config.endpoint}.`);
this.config.interval = ms;
this.start();
} else {
this.config.interval = ms;
}
return this;
}
// Start the interval function that polls the endpoint
start = () => {
if (this.fetchInterval) {
clearInterval(this.fetchInterval);
console.log(`DataManager: Already running! Clearing interval so it can be restarted.`);
}
this.fetchInterval = setInterval(async () => {
if (!this.fetching) {
console.log(`DataManager: Fetching data for endpoint "${this.config.endpoint}".`);
this.fetching = true;
// const res = await axios.get(this.config.endpoint);
// Commented out for demo purposes but you would uncomment this and clear the anonymous function below
const res = {};
(() => {
res.data = {
dataProp1: 1234,
dataProp2: 4567
}
})();
this.fetching = false;
this.data = res.data;
} else {
console.log(`DataManager: Waiting for pending response for endpoint "${this.config.endpoint}".`);
}
}, this.config.interval);
return this;
}
// Stop the interval function that polls the endpoint
stop = () => {
if (this.fetchInterval) {
clearInterval(this.fetchInterval);
console.log(`DataManager: Endpoint "${this.config.endpoint}" stopped.`);
} else {
console.log(`DataManager: Nothing to stop for endpoint "${this.config.endpoint}".`);
}
return this;
}
}
const SharedComponentState = {
source1: new DataManager({
interval: 1000,
endpoint: `/data/one/someId`,
autostart: true
}),
source2: new DataManager({
interval: 5000,
endpoint: `/data/two/someId`,
autostart: true
}),
source3: new DataManager({
interval: 10000,
endpoint: `/data/three/someId`,
autostart: true
})
};
setTimeout(() => { // For Demo Purposes, Stopping and starting DataManager.
SharedComponentState.source1.stop();
SharedComponentState.source1.updateInterval(2000);
SharedComponentState.source1.start();
}, 10000);
// Heres what it would look like to access the DataManager data (fetched from the api)
// You will need to make sure you pass the SharedComponentState object as a prop to the components or use another React mechanism for making that SharedComponentState accessible to the components in your app
// Accessing state for source 1: SharedComponentState.source1.data
// Accessing state for source 2: SharedComponentState.source2.data
// Accessing state for source 3: SharedComponentState.source3.data
基本上,DataManager class 的每个实例都负责获取不同的 api 端点。我包括了一些其他 class 方法,允许您启动、停止和更新 DataManager 实例的轮询频率。