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