Reputation: 33
I have a react component, I am updating a state in Mobx, and would like when this state is updated to reload the same component. I have a list of agencies, and I get the agency by type using query parameters within one component and based on the query parameter I want to get filtered data from DB.
My component code looks like this:
export interface IListAgenciesProps {
agencyType: string;
agencyTypeTitle: string;
}
const ListAgencies = (props: any) => {
let ctx = useContext(imsStore);
let agencies: IAgency[] = [];
useEffect(() => {
const agencyType = props.match.params["AgencyType"];
ctx.setAgency(agencyType);
async function agencyLoad(){
await ctx.loadAgencies();
agencies = ctx.agencies;
}
agencyLoad();
},
[agencies]);
let html: JSX.Element;
if (agencies.length > 0) {
html = ( <Container>
<Table celled selectable>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Agency Name</Table.HeaderCell>
<Table.HeaderCell>Telephone</Table.HeaderCell>
<Table.HeaderCell>Email</Table.HeaderCell>
<Table.HeaderCell>Is Active?</Table.HeaderCell>
<Table.HeaderCell>Hourly Rate</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{agencies.map(a => {
return (
<Table.Row>
<Table.Cell>{a.name}</Table.Cell>
<Table.Cell>{a.telephone}</Table.Cell>
<Table.Cell>{a.email}</Table.Cell>
<Table.Cell>{a.isActive}</Table.Cell>
<Table.Cell>{a.pricePerHour}</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
</Container>
);
} else {
html = ( <Container>
<Header as='h1'>
Sorry, we couldn't find the agency type you're looking for.
</Header>
</Container>
);
}
return <>{html}</>;
};
export default observer(ListAgencies);
In my IMSStore.ts, it looks like this:
@observable agencies: IAgency[] = [];
@action loadAgencies = async () => {
this.agencies = [];
try {
const agencies = await agent.Agencies.list(this.agencyType);
agencies.forEach(agency => {
this.agencies.push(agency);
});
} catch (err) {
console.log(err);
} finally {
}
};
However, the agencies array in my first code snippet, is always empty when the return statement is executed. So everytime the useEffect runs, it calls the loadAgencies, and in loadAgencies it resets the value of the agencies to an empty array. Then the return statement is executed. How do I make the return statement to wait until the agencies array are actually filled?
Upvotes: 0
Views: 1259
Reputation: 388
Ah, ok if you wanna keep it relatively similar, let me suggest a possible simplification for ya:
export interface IListAgenciesProps {
agencyType: string;
agencyTypeTitle: string;
}
const ListAgencies = (props: any) => {
const agencyType = props.match.params["AgencyType"];
const ctx = useContext(imsStore);
const { agencies, loadAgencies } = ctx;
useEffect(() => {
loadAgencies(agencyType);
}, [agencyType]);
let html: JSX.Element;
if (agencies.length > 0) {
html = ( <Container>
<Table celled selectable>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Agency Name</Table.HeaderCell>
<Table.HeaderCell>Telephone</Table.HeaderCell>
<Table.HeaderCell>Email</Table.HeaderCell>
<Table.HeaderCell>Is Active?</Table.HeaderCell>
<Table.HeaderCell>Hourly Rate</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
{agencies.map(a => {
return (
<Table.Row>
<Table.Cell>{a.name}</Table.Cell>
<Table.Cell>{a.telephone}</Table.Cell>
<Table.Cell>{a.email}</Table.Cell>
<Table.Cell>{a.isActive}</Table.Cell>
<Table.Cell>{a.pricePerHour}</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</Table>
</Container>
);
} else {
html = ( <Container>
<Header as='h1'>
Sorry, we couldn't find the agency type you're looking for.
</Header>
</Container>
);
}
return <>{html}</>;
};
export default observer(ListAgencies);
Store:
@observable agencies: IAgency[] = [];
@action loadAgencies = async (agencyType) => {
try {
this.agencies = await agent.Agencies.list(agencyType);
} catch (err) {
console.log(err);
} finally {
}
};
So what's happening now is we're simplifying the component so it saves the agencyType
on mount. Then specifying it in the deps array for the effect means that we call loadAgencies
everytime the params change. Notice we're now calling the ctx
method with a parameter. So loadAgencies
is gonna take in that type, go fetch the data, and then set it to the context variable all inside the same @action
.
Then, since we've dereferenced agencies
from ctx
inside the component, it will re-render when the agencies
@observable
is updated. We don't need to make the call inside the effect async - the component will re-render whenever the data comes back.
Upvotes: 1