I have an array in “State” of parent component called “portfolios”. You can access by using: “this.state.portfolios”.
And a child component called “StockDetailsComponent”.
In parent component, I’m using map function to render child component “StockDetailsComponent” like this:
{
this.state.portfolios.map((obj, key) => {
return <StockDetailsComponent key={key} portfolio={obj} onRemovePortfolio={this.onRemovePortfolio}/>;
})
}
It’s ok. But when I reorder “this.state.portfolios”, child component re-render not as expected.
Before: portfolios = [“object_stock_symbol_1”, “object_stock_symbol_2”];
After re-order: portfolios = [“object_stock_symbol_2”, “object_stock_symbol_1”];
Parent component looks like as below and lets me explan:
class VndDomain extends React.Component {
constructor(props) {
super(props);
this.socket = null;
this.sortableEnabled = false;
this.state = {
portfolios: []
};
}
render() {
return (
<div id="SQ-vnd">
<HeaderComponent/>
<div className="SQ-body">
<div className="SQ-text-right">
<p className="SQ-d-inline-block SQ-cursor-pointer SQ-mt-5 SQ-mb-5" onClick={this.removeAllPortfolios}>Xóa toàn bộ</p>
</div>
<StockFormCreateComponent onCreatePortfolio={this.onCreatePortfolio}/>
<div id="JSSQ-portfolio">
{
this.state.portfolios.map((obj, key) => {
return <StockDetailsComponent key={key} portfolio={obj} onRemovePortfolio={this.onRemovePortfolio}/>;
})
}
</div>
</div>
<FooterComponent/>
</div>
);
}
componentDidMount() {
this.enableSortable();
this.getAllPortfolioByUserId();
}
/**
* Get all portfolios belong to current user
* @return {Promise} [description]
*/
getAllPortfolioByUserId = async () => {
try {
this.props.dispatch(loadingSpinnerActions.showLoadingSpinner());
let result = await PortfolioService.getAllPortfolioByUserId();
if(result.data.status === "SUCCESSFUL") {
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
} else {
throw new Error(`${result.data.message}`);
}
} catch(error) {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
CommonUtilities.ShowLog(error.message);
}
}
/**
* Enable drag and drop to reorder
* @return {[type]} [description]
*/
enableSortable = () => {
let parentEl = $("#JSSQ-portfolio");
// Check duplicate sortable before set
if (this.sortableEnabled) {
parentEl.sortable("destroy");
parentEl.sortable();
} else {
parentEl.sortable();
this.sortableEnabled = true;
}
parentEl.on("sortupdate", async () => {
let sorted = parentEl.sortable("serialize");
let newOrderArrObj = sorted.split("&").map((value) => {
let symbol = value.replace("SQ[]=", "").toUpperCase();
let portfolio = this.state.portfolios.find(obj => obj.symbol === symbol);
return {
_id: portfolio._id,
symbol
};
});
try {
this.props.dispatch(loadingSpinnerActions.showLoadingSpinner());
let result = await PortfolioService.reorderPortfolio({newOrder: newOrderArrObj});
if(result.data.status === "SUCCESSFUL") {
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
} else {
throw new Error(`${result.data.message}`);
}
} catch(error) {
this.setState((prevState) => {
return {portfolios: prevState.portfolios};
}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
}
});
}
First, I get list Portfolios from the database, assign to “portfolios” of state, shown in the Client. And enable drag/drop to re-order by “enableSortable” function. At this time, it’s working fine.
When I drag to re-order, “this.state.portfolios” changed as expected, I can see in “console.log()” but child component render is wrong. Not as order.
The code is very long, so you only need to pay attention to the following options I tried: Option 1:
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
result.data.data is data after re-ordered, it’s fine but re-render not work as order.
Option 2: If I clear state by an empty array and set it again like code below, it’s work because child component has been “Unmount” and re-render instead just update like Option 1.
this.setState({portfolios: []}, () => {
this.setState({portfolios: result.data.data}, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
});
Please help me 🙁 I don’t want to setState and then setState again.
Advertisement
Answer
It looks like that your portfolio data is a complex object which React is not able to determine that has changed. Yoiu can do like below:
You can do like :
let newPrtfolio = Object.assign ([],result.data.data); // Assign data to new object and assign it
this.setState({portfolios: newPrtfolio }, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
or if your portfolio is an object and not an array than you may try below way:
let newPrtfolio = Object.assign ({},result.data.data); // Assign data to new object and assign it
this.setState({portfolios: newPrtfolio }, () => {
this.props.dispatch(loadingSpinnerActions.hideLoadingSpinner());
});
You may try both the ways, and one of them will work out for you depending upon the structure of your portfolio object