Я занимаюсь исследованиями и разработками, и решил изучить React и создать приложение, делая это.
Я столкнулся со специфической проблемой, и это заставляет меня чесать голову. Я не смог найти точное совпадение с моей проблемой уже в Stackoverflow, поэтому подумал, что сделаю сообщение.
Tl; dr проблемы: сделать вызов Ax ios вне моего компонента и затем установить состояние не будет вызывать повторное рендеринг, но выполнение вызова Ax ios внутри компонента делает это.
Я переименовал некоторые части кода.
Сводка приложения
Я создал интерфейсную часть приложения MVC, которое будет общаться с сервером Spring Boot моего коллеги, чтобы получить данные "config", которые будут извлечены из базы данных SQL. Мой веб-интерфейс отобразит эти данные конфигурации и позволит их редактировать.
Я использую Ax ios для вызовов REST.
У меня есть вызов Ax ios в componentDidMount () одного из моих компонентов, который выполняет первоначальный вызов на сервер и затем добавляет эти результаты в состояние этого компонента. Страница перерисовывается и таблица заполняется результатами. Таблица является собственным компонентом, а каждая запись также имеет свой собственный компонент. Данные передаются в эти компоненты через реквизиты.
Проблема
Если я оставлю вызов Ax ios в componentDidMount (), тогда мое приложение будет работать как положено.
Тем не менее, у меня есть несколько компонентов, выполняющих вызовы REST, поэтому есть дублированный код, поэтому я хотел по сути создать REST-сервис в моем приложении, который может вызывать любой компонент. Я сделал это, следуя шаблону проектирования Command, и даже написал его в Typescript. Избыток знаю, но это для НИОКР, и я просто хотел сделать это как учебное упражнение.
Итак, чтобы использовать это, у меня есть функция в моем компоненте, которая устанавливает команду, вызывающий с командой и url, а затем вызывает execute для вызывающего и добавляет результаты в переменную.
Итак, вот в чем проблема: если я использую свой сервис REST, в котором вызов Ax ios находится в классе вне мой компонент, то повторное отображение не происходит, и моя таблица не заполнена данными. Но если я сохраню вызов Ax ios внутри моего componentDidMount (), то все будет работать нормально.
Что я пробовал
После игры с большим количеством консоли .logs Я видел, что setState не получал данные до того, как установил состояние, поэтому мои «элементы» в состоянии были пустыми. Я решил это, добавив 'asyn c' к функции и 'await' к вызову setState. Теперь я вижу через console.log, что состояние имеет данные, однако повторное рендеринг по-прежнему не срабатывает.
Я также пытался что-то изменить - поэтому у меня была функция и все в componentDidMount (), затем я переместил все и просто вызвал эту функцию в componentDidMount () - это не сработало.
Мой код
maincomponent. js Это компонент, который инициирует вызов Ax ios. Вы увидите, что мой исходный вызов Ax ios закомментирован.
class ParameterList extends React.Component {
constructor(props) {
super(props)
this.state = {
error: null,
isLoaded: false,
items: [],
}
}
componentDidMount() {
// Axios.get(URLS.GET_CONFIG_PARAMETERS).then((results) => {
// console.log(results.data)
// this.setState({
// items: results.data
// })
// })
this.retrieveCONFIGParameters()
}
async retrieveCONFIGParameters() {
const serviceCommand = new GetServiceCommand()
const invoker = new ServiceInvoker(serviceCommand, URLS.GET_CONFIG_PARAMETERS)
const returnData = invoker.ExecuteRequest()
await this.setState({
items: returnData
}, function() {
console.log(this.state.items)
});
console.log(this.state.items)
}
render() {
return (
<div>
<h1>Parameter List</h1>
<Container>
<Grid celled>
<Grid.Row>
<Label attached='top left' color='blue'>Current EDI Message Status</Label>
<Grid.Column width={16}>
<Table celled structured color='blue'>
<Table.Header>
<Table.Row>
<Table.HeaderCell><Radio label='Active'></Radio></Table.HeaderCell>
<Table.HeaderCell><Radio label='Inactive'></Radio></Table.HeaderCell>
<Table.HeaderCell><Radio label='All'></Radio></Table.HeaderCell>
</Table.Row>
<Table.Row>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
<Table.HeaderCell>XXXX</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{this.state.items.map((param, i) => {
if (param.active === true) {
return <ParameterListItem key={i} paramData={param} />
} else {
return false // TODO
}
}
)}
</Table.Body>
</Table>
</Grid.Column>
</Grid.Row>
<Grid.Row>
<Grid.Column width={16}>
<Button color='teal'>
<Button.Content>Add</Button.Content>
</Button>
<Button color='teal'>
<Button.Content>Modify</Button.Content>
</Button>
<Button color='teal'>
<Button.Content>Remove</Button.Content>
</Button>
<Button color='teal'>
<Button.Content>Print</Button.Content>
</Button>
<Button color='teal'>
<Button.Content>Position</Button.Content>
</Button>
<Button color='teal'>
<Button.Content>Re-Instate</Button.Content>
</Button>
</Grid.Column>
</Grid.Row>
</Grid>
</Container>
<br></br>
</div>
);
}
}
GetServiceCommand.ts Это команда GET с Ax ios вызов.
export class GetServiceCommand implements IServiceCommand {
returnData = [] as any
execute(serviceURL: string): Object {
Axios.get(serviceURL).then(results => {
console.log(results.data)
results.data.forEach((element: any) => {
this.returnData.push(element)
});
})
console.log(this.returnData)
return this.returnData
}
}
При необходимости я могу предоставить код из других классов, но это просто стандартные шаблоны команд, поэтому я не думаю, что это
Заключительные мысли
Я знаю, что то, что я сделал, вероятно, излишне, и что возвращение к простому использованию вызовов Ax ios внутри каждого компонента приведет к устранить эту проблему go. Но я несколько дней думал об этом, и мне просто хотелось бы узнать, что я сделал не так.
И еще одна вещь: console.log для данных из вызова Ax ios и со стороны выглядят иначе. С самого вызова Ax ios данные выглядят так:
(6) [{…}, {…}, {…}, {…}, {…}, {…}]
0: {code: "8", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
1: {code: "5", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
2: {code: "2", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
3: {code: "6", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
4: {code: "6", active: false, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
5: {code: "6", active: false, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
length: 6
А из состояния:
[]
0: {code: "8", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
1: {code: "5", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
2: {code: "2", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
3: {code: "6", active: true, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
4: {code: "6", active: false, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
5: {code: "6", active: false, description: "Sample Param Desc", XXXX: "System1", networkId: "Network1", …}
length: 6
Вы заметите, что состояние начинается с [], тогда как данные Ax ios (6). Я не уверен, что именно это является причиной проблемы ... Я думаю, что это маловероятно, потому что это просто массивы, но я решил упомянуть об этом на всякий случай.
Спасибо всем, кто нашел время чтобы посмотреть на это.
РЕДАКТИРОВАТЬ
Спасибо всем, кто предложил свой совет. Теперь мне удалось решить эту проблему, хотя она выглядит немного грязной.
В предложениях была подчеркнута проблема с вызовом Ax ios и возвратом - после некоторого времени, проведенного с кучей console.logs all во время шоу я понял, что Return в GetServiceCommand на самом деле пуст; это сбивает с толку, потому что другие console.logs показывают, что у них есть данные, как только они попадают в компонент, но это не может быть правильно, потому что Return запускается до того, как данные добавляются в массив. Я думаю, что console.logs, по сути, вводят меня в заблуждение (или, точнее, я их неправильно понял).
Решение состоит в том, чтобы предоставить функцию-мутатор setState, которую можно передавать куда угодно.
Проблема в том, из-за того, что я использую Typescript, простой <Component functionMutator={this.functionToSetstate}
не будет работать, поэтому мне пришлось изменить свой слой REST, чтобы он принимал отдельный параметр, который является функцией как тип Function.
Затем измените GetServiceCommand, чтобы он ничего не возвращал, а вместо этого вызовите эту функцию с данными после завершения вызова Ax ios, аналогично предложению Qiara sh.
На всякий случай, если кому-то интересно, вот мой обновленный код:
maincomponent. js Здесь я создал функцию мутатора и привязал ее к 'this' в конструкторе , Затем я вызову REST внутри функции, которая вызывается в ComponentDidMount ()
setStateItems(items) {
this.setState({
items: items
}, function() {
console.log("setStateItems called")
})
}
constructor(props) {
super(props)
this.state = {
error: null,
isLoaded: false,
items: [],
}
this.setStateItems = this.setStateItems.bind(this)
}
retrieveCONFIGParameters() {
const serviceCommand = new GetServiceCommand()
const invoker = new ServiceInvoker(serviceCommand, EdiUrls.GET_CONFIG_PARAMETERS, this.setStateItems)
invoker.ExecuteRequest()
}
GetServiceCommand.ts Вы заметите, что вместо Return теперь просто вызов функции, переданной из основного компонента. js
export class GetServiceCommand implements IServiceCommand {
execute(serviceURL: string, functionToCall: Function) {
let returnData: Array<string>;
returnData = []
Axios.get(serviceURL).then(results => {
console.log(results.data)
results.data.forEach((element: any) => {
console.log("test4")
returnData.push(element)
});
functionToCall(returnData)
})
}
}
Еще раз спасибо за вашу помощь и предложения. Я не уверен, что это хороший способ добиться этого, но я, безусловно, многое узнал о React, state и Typescript.