我如何减慢我的快速服务器响应以允许 react-admin getOne() 函数工作?
How do I slow down my express server response to allow the react-admin getOne() function to work?
我已经创建了 VetCreate 和 VetEdit 函数来创建新记录,然后分别编辑该记录(代码如下)。我遇到的问题是返回了成功的创建响应,但新创建的 ID 未填充到获取记录的请求中。
我在我认为需要的地方设置了 async/await
关键字,但记录到控制台的详细信息清楚地表明某些地方没有正常工作。如果我在返回列表屏幕后尝试编辑记录,API 会按预期工作,returns 详细信息。
我已经添加了我能想到的所有代码和屏幕截图,但如果需要更多内容,请询问。
VetCreate
export const VetCreate = (props) => (
<Create title="Credit Application" aside={<CreateAside />} {...props}>
<TabbedForm toolbar={<VetCreateToolbar />} redirect="show">
<FormTab label="applicant">
<DateInput disabled label="Date Created" source="DateCreated" defaultValue={moment(new Date()).format('YYYY-MM-DD hh:mm:ss')} />
<TextInput disabled label="Agent Name" defaultValue={sessionStorage.getItem('foneBookUser')} source="CreatedBy" />
<TextInput label="First Name" defaultValue={'Adam'} source="FirstName" validate={validateLength} />
<TextInput label="Surname" defaultValue={'Smith'} source="Surname" validate={validateLength} />
<TextInput label="RSA ID Number" defaultValue={4567890987654} source="IDNumber" validate={validateID} />
<SelectInput label="Sex" defaultValue={'M'} source="sex" choices={[
{ id: 'M', name: 'Male' },
{ id: 'F', name: 'Female' },
]} validate={validateSex}/>
<TextInput label="Age of Applicant" defaultValue={45} source="ApplicantAge" validate={validateAge} />
<TextInput label="Business Partner" defaultValue={''} source="BusinessPartner" />
<TextInput label="Sales Person" defaultValue={'Spiderman'} source="SalesPerson" validate={validateLength} />
<TextInput label="Cell Phone Number" defaultValue={'345678'} source="CellNumber" validate={validateLength} />
<TextInput label="Email Address" defaultValue={'adam@email.com'} source="Email" validate={validateEmail} />
</FormTab>
<FormTab label="bureau">
<TextInput label="Bureau Score" defaultValue={123} source="BureauScore" validate={validateBureauScore} />
<TextInput label="Gross Monthly Income" defaultValue={30000} source="GrossMonthlyIncome" validate={validateGross} />
<TextInput label="Total Monthly Instalment" defaultValue={3000} source="TotalMonthlyInstalment" validate={validateInstal} />
<TextInput label="Home Postcode" defaultValue={'1122'} source="HomePostCode" validate={validateCode} />
<TextInput label="Number of Properties" defaultValue={11} source="NumProperties" validate={validateNumber} />
<TextInput label="Number of Companies" defaultValue={31} source="NumCompanies" validate={validateNumber} />
</FormTab>
</TabbedForm>
</Create>
);
VetEdit
export const VetEdit = props => {
const classes = useStyles();
return (
<Edit aside={<EditAside />} {...props}>
<SimpleForm toolbar={<VetEditToolbar />} warnWhenUnsavedChanges>
<TextInput disabled label="First Name" source="FirstName" formClassName={classes.inlineBlock} />
<TextInput disabled label="Surname" source="Surname" formClassName={classes.inlineBlock} />
<TextInput disabled label="RSA ID Number" source="IDNumber" formClassName={classes.inlineBlock} />
<TextInput disabled source="Sex" formClassName={classes.inlineBlock} />
<NumberInput disabled source="ApplicantAge" formClassName={classes.inlineBlock} />
<TextInput disabled source="CellNumber" formClassName={classes.inlineBlock} />
<TextInput disabled source="Email" formClassName={classes.inlineBlock} />
<TextInput disabled source="BusinessPartner" formClassName={classes.inlineBlock} />
<TextInput disabled source="SalesPerson" formClassName={classes.inlineBlock} />
<TextInput disabled source="HomePostCode" formClassName={classes.inlineBlock} />
<NumberInput disabled source="NumProperties" formClassName={classes.inlineBlock} />
<NumberInput disabled source="NumCompanies" formClassName={classes.inlineBlock} />
<NumberInput disabled source="GrossMonthlyIncome" formClassName={classes.inlineBlock} />
<NumberInput disabled source="TotalMonthlyInstalment" formClassName={classes.inlineBlock} />
<NumberInput disabled source="BureauScore" formClassName={classes.inlineBlock} />
<SelectInput label="ID Verified" source="IDVerified" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Debt Review" source="DebtReview" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Poor Payment Profile" source="PoorPaymentProfile" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Adverse" source="Adverse" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Fraud Suspected" source="SuspectFraud" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<TextInput label="ID Validated" source="IDValidated" formClassName={classes.inlineBlock} />
<TextInput label="Bank Statement Validated" source="BankStatementValidated" formClassName={classes.inlineBlock} />
<TextInput label="Payslip Validated" source="PayslipValidated" formClassName={classes.inlineBlock} />
<TextInput label="Proof of Residence Validated" source="ResProofValidated" formClassName={classes.inlineBlock} />
</SimpleForm>
</Edit>
)
};
DataProvider.js
import { fetchUtils } from 'react-admin';
import { stringify } from 'query-string';
const apiUrl = 'http://localhost:8081';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json'
});
}
// add your own headers here
options.headers.set(
'X-Custom-Header',
'Access-Control-Allow-Headers',
'Access-Control-Allow-Origin',
'*',
);
//console.log('4 options: ', options);
return fetchUtils.fetchJson(url, options);
};
export default {
getList: (resource, params) => {
//console.log('params: ', params);
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify(params.filter),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
//console.log('url: ', url);
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
}));
},
getOne: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
data: json,
})),
getMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ json }) => ({ data: json }));
},
getManyReference: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify({
...params.filter,
[params.target]: params.id,
}),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
}));
},
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json })),
updateMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
delete: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json })),
deleteMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'DELETE',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
}
};
Vet API
Vet.createVet = async function(newVet, result) {
console.log('newVet: ', newVet);
await sql.query("INSERT INTO vets set ?", newVet, function(err, res) {
if(err) {
console.log('err: ', err);
result(err, null);
} else {
console.log('res.insertId: ', res.insertId);
result(null, res.insertId)
}
});
};
Vet.getVetById = async function (VetId, result) {
console.log('getVetById: ', VetId);
await sql.query("Select * from vets WHERE id = ?", VetId, function (err, res) {
if(err) {
console.log("error: ", err);
result(null, err);
} else {
console.log('vets : ', res);
result(null, res);
}
});
};
await
只有当你 await
承诺时才会做一些有用的事情。像这样的语句:
await sql.query("INSERT INTO vets set ?", newVet, function(err, res) {...});
不等待承诺。该查询没有 return 承诺。该操作的结果仅出现在该回调中,而不出现在任何 returned promise 中,因此 await
无关。 await
中没有魔法可以知道普通回调何时完成。不,await
以承诺为基础运作,如果你不给它承诺,它就无事可做(不等待任何事情)。
相反,您需要使用不使用回调的数据库操作,而是 return promises 如果您想对它们使用 await
。
如果您正在使用 mysql,请参阅 mysql2 以获取承诺支持,然后重写所有数据库语句以使用 returned 承诺从中获取结果并且不通过数据库调用一个简单的回调。
万一其他人遇到这个问题并且不知道 promise - async/await 关系,我发现 this 文章非常有帮助。
我已经创建了 VetCreate 和 VetEdit 函数来创建新记录,然后分别编辑该记录(代码如下)。我遇到的问题是返回了成功的创建响应,但新创建的 ID 未填充到获取记录的请求中。
我在我认为需要的地方设置了 async/await
关键字,但记录到控制台的详细信息清楚地表明某些地方没有正常工作。如果我在返回列表屏幕后尝试编辑记录,API 会按预期工作,returns 详细信息。
我已经添加了我能想到的所有代码和屏幕截图,但如果需要更多内容,请询问。
VetCreate
export const VetCreate = (props) => (
<Create title="Credit Application" aside={<CreateAside />} {...props}>
<TabbedForm toolbar={<VetCreateToolbar />} redirect="show">
<FormTab label="applicant">
<DateInput disabled label="Date Created" source="DateCreated" defaultValue={moment(new Date()).format('YYYY-MM-DD hh:mm:ss')} />
<TextInput disabled label="Agent Name" defaultValue={sessionStorage.getItem('foneBookUser')} source="CreatedBy" />
<TextInput label="First Name" defaultValue={'Adam'} source="FirstName" validate={validateLength} />
<TextInput label="Surname" defaultValue={'Smith'} source="Surname" validate={validateLength} />
<TextInput label="RSA ID Number" defaultValue={4567890987654} source="IDNumber" validate={validateID} />
<SelectInput label="Sex" defaultValue={'M'} source="sex" choices={[
{ id: 'M', name: 'Male' },
{ id: 'F', name: 'Female' },
]} validate={validateSex}/>
<TextInput label="Age of Applicant" defaultValue={45} source="ApplicantAge" validate={validateAge} />
<TextInput label="Business Partner" defaultValue={''} source="BusinessPartner" />
<TextInput label="Sales Person" defaultValue={'Spiderman'} source="SalesPerson" validate={validateLength} />
<TextInput label="Cell Phone Number" defaultValue={'345678'} source="CellNumber" validate={validateLength} />
<TextInput label="Email Address" defaultValue={'adam@email.com'} source="Email" validate={validateEmail} />
</FormTab>
<FormTab label="bureau">
<TextInput label="Bureau Score" defaultValue={123} source="BureauScore" validate={validateBureauScore} />
<TextInput label="Gross Monthly Income" defaultValue={30000} source="GrossMonthlyIncome" validate={validateGross} />
<TextInput label="Total Monthly Instalment" defaultValue={3000} source="TotalMonthlyInstalment" validate={validateInstal} />
<TextInput label="Home Postcode" defaultValue={'1122'} source="HomePostCode" validate={validateCode} />
<TextInput label="Number of Properties" defaultValue={11} source="NumProperties" validate={validateNumber} />
<TextInput label="Number of Companies" defaultValue={31} source="NumCompanies" validate={validateNumber} />
</FormTab>
</TabbedForm>
</Create>
);
VetEdit
export const VetEdit = props => {
const classes = useStyles();
return (
<Edit aside={<EditAside />} {...props}>
<SimpleForm toolbar={<VetEditToolbar />} warnWhenUnsavedChanges>
<TextInput disabled label="First Name" source="FirstName" formClassName={classes.inlineBlock} />
<TextInput disabled label="Surname" source="Surname" formClassName={classes.inlineBlock} />
<TextInput disabled label="RSA ID Number" source="IDNumber" formClassName={classes.inlineBlock} />
<TextInput disabled source="Sex" formClassName={classes.inlineBlock} />
<NumberInput disabled source="ApplicantAge" formClassName={classes.inlineBlock} />
<TextInput disabled source="CellNumber" formClassName={classes.inlineBlock} />
<TextInput disabled source="Email" formClassName={classes.inlineBlock} />
<TextInput disabled source="BusinessPartner" formClassName={classes.inlineBlock} />
<TextInput disabled source="SalesPerson" formClassName={classes.inlineBlock} />
<TextInput disabled source="HomePostCode" formClassName={classes.inlineBlock} />
<NumberInput disabled source="NumProperties" formClassName={classes.inlineBlock} />
<NumberInput disabled source="NumCompanies" formClassName={classes.inlineBlock} />
<NumberInput disabled source="GrossMonthlyIncome" formClassName={classes.inlineBlock} />
<NumberInput disabled source="TotalMonthlyInstalment" formClassName={classes.inlineBlock} />
<NumberInput disabled source="BureauScore" formClassName={classes.inlineBlock} />
<SelectInput label="ID Verified" source="IDVerified" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Debt Review" source="DebtReview" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Poor Payment Profile" source="PoorPaymentProfile" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Adverse" source="Adverse" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<SelectInput label="Fraud Suspected" source="SuspectFraud" choices={[
{ id: 'N', name: 'No' },
{ id: 'Y', name: 'Yes'}
]} formClassName={classes.inlineBlock} />
<TextInput label="ID Validated" source="IDValidated" formClassName={classes.inlineBlock} />
<TextInput label="Bank Statement Validated" source="BankStatementValidated" formClassName={classes.inlineBlock} />
<TextInput label="Payslip Validated" source="PayslipValidated" formClassName={classes.inlineBlock} />
<TextInput label="Proof of Residence Validated" source="ResProofValidated" formClassName={classes.inlineBlock} />
</SimpleForm>
</Edit>
)
};
DataProvider.js
import { fetchUtils } from 'react-admin';
import { stringify } from 'query-string';
const apiUrl = 'http://localhost:8081';
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({
'Accept': 'application/json',
'Content-Type': 'application/json'
});
}
// add your own headers here
options.headers.set(
'X-Custom-Header',
'Access-Control-Allow-Headers',
'Access-Control-Allow-Origin',
'*',
);
//console.log('4 options: ', options);
return fetchUtils.fetchJson(url, options);
};
export default {
getList: (resource, params) => {
//console.log('params: ', params);
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify(params.filter),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
//console.log('url: ', url);
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
}));
},
getOne: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`).then(({ json }) => ({
data: json,
})),
getMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids }),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ json }) => ({ data: json }));
},
getManyReference: (resource, params) => {
const { page, perPage } = params.pagination;
const { field, order } = params.sort;
const query = {
sort: JSON.stringify([field, order]),
range: JSON.stringify([(page - 1) * perPage, page * perPage - 1]),
filter: JSON.stringify({
...params.filter,
[params.target]: params.id,
}),
};
const url = `${apiUrl}/${resource}?${stringify(query)}`;
return httpClient(url).then(({ headers, json }) => ({
data: json,
total: parseInt(headers.get('content-range').split('/').pop(), 10),
}));
},
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json })),
updateMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'PUT',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
},
create: (resource, params) =>
httpClient(`${apiUrl}/${resource}`, {
method: 'POST',
body: JSON.stringify(params.data),
}).then(({ json }) => ({
data: { ...params.data, id: json.id },
})),
delete: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'DELETE',
}).then(({ json }) => ({ data: json })),
deleteMany: (resource, params) => {
const query = {
filter: JSON.stringify({ id: params.ids}),
};
return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, {
method: 'DELETE',
body: JSON.stringify(params.data),
}).then(({ json }) => ({ data: json }));
}
};
Vet API
Vet.createVet = async function(newVet, result) {
console.log('newVet: ', newVet);
await sql.query("INSERT INTO vets set ?", newVet, function(err, res) {
if(err) {
console.log('err: ', err);
result(err, null);
} else {
console.log('res.insertId: ', res.insertId);
result(null, res.insertId)
}
});
};
Vet.getVetById = async function (VetId, result) {
console.log('getVetById: ', VetId);
await sql.query("Select * from vets WHERE id = ?", VetId, function (err, res) {
if(err) {
console.log("error: ", err);
result(null, err);
} else {
console.log('vets : ', res);
result(null, res);
}
});
};
await
只有当你 await
承诺时才会做一些有用的事情。像这样的语句:
await sql.query("INSERT INTO vets set ?", newVet, function(err, res) {...});
不等待承诺。该查询没有 return 承诺。该操作的结果仅出现在该回调中,而不出现在任何 returned promise 中,因此 await
无关。 await
中没有魔法可以知道普通回调何时完成。不,await
以承诺为基础运作,如果你不给它承诺,它就无事可做(不等待任何事情)。
相反,您需要使用不使用回调的数据库操作,而是 return promises 如果您想对它们使用 await
。
如果您正在使用 mysql,请参阅 mysql2 以获取承诺支持,然后重写所有数据库语句以使用 returned 承诺从中获取结果并且不通过数据库调用一个简单的回调。
万一其他人遇到这个问题并且不知道 promise - async/await 关系,我发现 this 文章非常有帮助。