Reputation: 777
I am having some trouble dealing with an async issue. The render is happening before the state is set at getStepContent(0) causing me to lose access to the state's values when I pass it down a component (CartInfo). Any ideas?
class Cart extends PureComponent {
constructor(props) {
super(props);
this.state = {
order: [],
error: null,
finished: false,
stepIndex: 0
};
}
componentWillMount() {
Meteor.call("orders.getLastOrder", (error, response) => {
if (error) {
this.setState(() => ({ error: error }));
console.log(error);
} else {
this.setState(() => ({ order: response }));
console.log(this.state.order);
}
});
}
goBack = () => this.props.history.push("/shop");
goCart = () => this.props.history.push("/cart");
handleNext = () => {
const { stepIndex } = this.state;
this.setState({
stepIndex: stepIndex + 1,
finished: stepIndex >= 2
});
};
handlePrev = () => {
const { stepIndex } = this.state;
if (stepIndex > 0) {
this.setState({ stepIndex: stepIndex - 1 });
}
};
getStepContent(stepIndex) {
let { order } = this.state;
switch (stepIndex) {
case 0:
while (!order) {
return getStepContent(0);
}
return <CartInfo CartInfo={order} />;
case 1:
return "What is an ad group anyways?";
case 2:
return "This is the bit I really care about!";
default:
return "You're a long way from home sonny jim!";
}
}
render() {
const { finished, stepIndex, order } = this.state;
const contentStyle = { margin: "0 16px" };
return (
<CartPage pageTitle="Cart" history goBack={this.goBack}>
<div className="CartHomePage">
<div style={{ width: "100%", maxWidth: 700, margin: "auto" }}>
<Stepper activeStep={stepIndex}>
<Step>
<StepLabel>Confirm your order</StepLabel>
</Step>
<Step>
<StepLabel>Where should we send it to?</StepLabel>
</Step>
<Step>
<StepLabel>Enjoy!</StepLabel>
</Step>
</Stepper>
<div style={contentStyle}>
{finished
? <p>
<a
href="#"
onClick={event => {
event.preventDefault();
this.setState({ stepIndex: 0, finished: false });
}}
>
Click here
</a>{" "}
to reset the example.
</p>
: <div>
{this.getStepContent(stepIndex)}
<div style={{ marginTop: 12 }}>
<FlatButton
label="Back"
disabled={stepIndex === 0}
onClick={this.handlePrev}
style={{ marginRight: 12 }}
/>
<RaisedButton
label={stepIndex === 2 ? "Finish" : "Next"}
primary={true}
onClick={this.handleNext}
/>
</div>
</div>}
</div>
</div>
<div>
{/* {order.map((item, i) => <div key={i}> {item.name}
{item.price} {item.quantity}</div>)} */}
{/* {this.state.order[0]} */}
</div>
</div>
</CartPage>
);
}
}
export default Cart;
This is the component I am passing it on to
import React from "react";
import { withRouter } from "react-router";
const CartInfo = ({ CartInfo }) =>
<div>
{CartInfo[0].name}
</div>;
export default withRouter(CartInfo);
This is the error code I am currently getting "CartInfo.jsx:6 Uncaught TypeError: Cannot read property 'name' of undefined at CartInfo"
Upvotes: 1
Views: 213
Reputation: 1567
It looks like you are trying to access CartInfo[0].name
before the data has been fetched, which throws an error. You can change CartInfo component to something like this:
const CartInfo = ({ CartInfo }) => {
if (CartInfo[0]) {
return(
<div>
{CartInfo[0].name}
</div>;
);
}
}
This way the component will return null, then when the order data is fetched it will rerender and CartInfo[0]
will not be undefined.
Another way to do this would be to use lodash _.get
which returns undefined instead of throwing an error when you try to access properties of undefined.
Upvotes: 1