最近專案中使用redux-form遇上效能瓶頸,
需要100行以上聯動表單變得極其卡
原因
- 元件過多渲染導致! 更改一個輸入框導致所有輸入框都重新渲染。
解決思路
-
Field 使用 PureComponent 減少不必要的渲染[如果無效用shouldComponentUpdate ]
-
推薦使用shouldComponentUpdate 精確控制到每行及每個輸入框的渲染
-
覆蓋掉redux-from的onChange事件 改用onFocus和onBlur
下面程式碼我在FormMembers裡遍歷了fields,所以FormMembers重新渲染時會導致fields全部重新渲染。
優化後使用shouldComponentUpdate控制到每一行及每一個輸入框的重新渲染,提高了非常多的效能!
優化前程式碼及結構
//表單
class Form extends Component{
render() {
return (
<div>
<form onSubmit={()=>{}}>
<table className="recipe_table" >
<FieldArray
name="members"
recipeName="herbal"
component={FormMembers}
change={change}
public_expense={public_expense}
/>
</table>
</form>
</div>)
}
}
//Members
class FormMembers extends Component{
render() {
let {fields}=this.props;
return(
<tbody>
{fields.map((item,key)=>{
console.log('//此處省略大量計算')
return (
<tr key={key} >
<td>
<Field name={`${item}.name`} component={Field}/>
</td>
</tr>
)
})}
</tbody>)
}
}
//數字輸入框
class Field extends Component{
render(){
let {input}=this.props;
return <input {...input} />
}
}
複製程式碼
優化後
import isEqual from "lodash/isEqual";
//表單
class Form extends Component{
render() {
return (
<div>
<form onSubmit={()=>{}}>
<table className="recipe_table" >
<FieldArray
name="members"
recipeName="herbal"
component={FormMembers}
change={change}
public_expense={public_expense}
/>
</table>
</form>
</div>)
}
}
//Members
class FormMembers extends Component{
render() {
let {fields}=this.props;
return(
<tbody>
{fields.map((item,key)=>(<Line item={item} lineKey={key} />))}
</tbody>)
}
}
//每行重新整理控制
class Line extends Component{
constructor(props){
super(props)
this.currData={};
}
shouldComponentUpdate(nextProps,nextState){//改用手動控制每行渲染
if(!isEqual(nextProps.fields.get(nextProps.lineKey),this.currData)){
return true;
}
return false;
}
render(){
this.currData=this.props.get(this.props.lineKey)
console.log('//此處省略大量計算')
return (
<tr key={this.props.lineKey} >
<td>
<Field name={`${item}.name`} component={Field}/>
</td>
</tr>)
}
}
//數字輸入框
class Field extends Component{
shouldComponentUpdate(nextProps, nextState){//改用手動控制渲染
if(nextProps.input.name==this.props.input.name &&
nextProps.input.value==this.props.input.value &&
nextProps.className==this.props.className &&
nextProps.id==this.props.id &&
isEqual(nextProps.meta,this.props.meta)){
return false
}
return true
}
render(){
let {input}=this.props;
return <input {...input} />
}
}
複製程式碼