第一道菜:回鍋肉
react數(shù)組循環(huán)瞭吃,基本都會設(shè)置一個唯一的key马篮,表格的對象數(shù)組循環(huán)一般沒什么問題晤柄,數(shù)據(jù)基本都會有一個id粟判。那有種情況就比較坑了亿昏,出現(xiàn)在表單形式的頁面結(jié)構(gòu)中,對某個數(shù)組進(jìn)行增刪改操作档礁,一般對于非對象數(shù)組而言角钩,沒有id,可能很多人會偷懶呻澜,循環(huán)的時候递礼,直接設(shè)置數(shù)組的下標(biāo)index作為key,當(dāng)出現(xiàn)增刪改時候羹幸,就會出現(xiàn)數(shù)據(jù)對不上或者重新渲染組件的問題等脊髓。解決方案有很多種,例如把字符串?dāng)?shù)組等重組對象數(shù)組睹欲,每個元素設(shè)置一個唯一id等供炼。另外有個方式:推薦使用shortid生成唯一key的數(shù)組,和數(shù)據(jù)數(shù)組一起使用窘疮,省去提交數(shù)據(jù)時再重組數(shù)組袋哼。
importReactfrom'react';importshortidfrom'shortid';classDemoextendsReact.Component{constructor(props) {super(props);this.state={data:['a','b','c']}this.dataKeys=this.state.data.map(v=>shortid.generate());}deleteOne=index=>{//刪除操作const{data}=this.state;this.setState({ data:data.filter((v,i)=>i!==index) });this.dataKyes.splice(index,1);? ? }render() {return(<ul>{data.map((v,i)=><li? ? ? ? ? ? ? ? ? ? ? ? onClick={i=>this.deleteOne(i)}? ? ? ? ? ? ? ? ? ? ? ? ? key={this.dataKeys[i]}>{v}</li>)? ? ? ? ? ? ? }</ul>)}}//稍微抽取,可以封裝一個通用的組件
通過判斷值是否存在來控制元素是否顯示闸衫,一般三目運(yùn)算可以達(dá)到此效果涛贯,最簡單的還是用短路的寫法:
//不錯constflag='something';flag&&<div></div>//很好//注意一般可能上面寫法多一些,但當(dāng)flag為0 的時頁面上會顯示0蔚出,用!!將其轉(zhuǎn)為boolean避免坑弟翘,//代碼也更規(guī)范constflag='something';!!flag&&<div></div>
使用組件,傳遞props:
const{data,type,something}=this.state;<Demo? ? data={data}? ? type={type}? ? something={something}/>
也許另外一種傳遞方式更簡潔:
const { data, type, something } = this.state;
<Demo
? ? {...{ data, id, something }}
/>
組件的props有時候會定義很多骄酗,但是調(diào)用組件傳遞props的時候又想一個個傳稀余,不想一次性傳遞一個option對象,通過擴(kuò)展運(yùn)算符和解構(gòu)賦值可以簡化此操作:
const Demo = ({ prop1, prop2, prop3, ...restProps }) => (
? ? <div>
? ? ? ? xxxx
? ? ? ? { restProps.something }
? ? </div>
)
// 父組件使用Demo
<Demo
? ? prop1={xxx}
? ? prop2={xxx}
? ? something={xxx}
/>
一般改變state值的一種方式:
const{data}=this.state;this.setState({ data:{...data, key:1} });
另外一種可以通過callback的方式改變state的值
this.setState(({ data })=>({ data:{...data, key:1} }));
還可以:
this.setState((state,props)=>{return{ counter:state.counter+props.step};});
React 性能優(yōu)化有很多種方式趋翻,那常見的一種就是在生命周期函數(shù)shouldComponentUpdate里面判斷某些值或?qū)傩詠砜刂平M件是否重新再次渲染睛琳。
判斷一般的字符串,數(shù)字或者基礎(chǔ)的對象踏烙,數(shù)組都還是比較好處理师骗,那嵌套的對象或者數(shù)組就比較麻煩了,對于這種讨惩,可以轉(zhuǎn)成字符串處理辟癌,但屬性值的位置不同時,那就無效了荐捻。
推薦使用lodash(或者其他的類似庫)的isEqual對嵌套數(shù)組或?qū)ο筮M(jìn)行判斷(相比其他方式更簡單些)
shouldComponentUpdate(nextProps,nextState) {if(_.isEqual(nextState.columns,this.state.columns))returnfalse;returntrue;}
創(chuàng)建彈層的三種方式:
普通組件通過state和樣式控制黍少,在當(dāng)前組件中顯示彈層-每次引入組件并且render里面控制顯示寡夹,掛載節(jié)點(diǎn)在某組件里面
//彈層constDialog=()=><div>彈層</div>//某組件render() {return(this.state.showDialog&&<Dialog/>)}
2.通過Portals創(chuàng)建通道,在根節(jié)點(diǎn)外部掛載組件-但還是需要每次引入并且在render里面調(diào)用
//彈層classDialogextendsReact.Component{constructor(props) {super(props);this.el=document.createElement('div');? }componentDidMount() {modalRoot.appendChild(this.el);? }componentWillUnmount() {modalRoot.removeChild(this.el);? }render() {returnReactDOM.createPortal(this.props.children||<div>xxxx</div>,this.el,? ? );? }}//某組件render() {return(this.state.showDialog&&<Dialog/>)}
3.推薦使用ReactDom.render創(chuàng)建彈層-掛載根節(jié)點(diǎn)外層仍侥,使用也更方便
//demoletdialog;classDialog{show(children) {//顯示this.div=document.createElement('div');document.body.appendChild(this.div);ReactDom.render(children||<div>xxxx</div>,this.div);? ? }destroy() {//銷毀ReactDom.unmountComponentAtNode(this.div);this.div.parentNode.removeChild(this.div);? ? }}exportdefault{show:function(children) {? ? ? ? dialog=newDialog();dialog.show(children);? ? },? ? hide:xxxxx};//某組件importDialogfrom'xxx';alert=()=>{Dialog.show(xxxx);}render() {return(<button onClick={this.alert}>點(diǎn)擊彈層</button>)}
render props是現(xiàn)在很流行的一種渲染方式要出,通過回調(diào)函數(shù),渲染子組件农渊,參數(shù)可為父組件的任意屬性值(官網(wǎng)也有相應(yīng)的介紹)新版的contextApi也采用了這個模式患蹂。
很多種場景使用此方式的做法:
//權(quán)限控制組件,只需要封裝一次connect砸紊,//通過render props向子組件傳遞權(quán)限classAuthWidgetextendsComponent{render() {returnthis.props.children(this.props.auth);? ? }}constmapStateToProps=state=>{const{auth}=state;return{ auth:state.auth};};exportdefaultconnect(mapStateToProps)(AuthWidget);//其他組件使用<AuthWidget? ? children={auth=>auth.edit&&<a>編輯</a>}/>//使用antd的form時constTest=({ form, children })=>{returnchildren(form);};constFormTest=Form.create()(Test);classDemoextendsComponent{render() {return(<div>xxxxx<FormTest>{form=>{this.form=form;return(<Form><Form.Item>{getFieldDecorator('field', xxx)(<Input placeholder="請輸入鏈接地址"/>)}</Form.Item></Form>)? ? ? ? ? ? ? ? ? ? }}</FormTest></div>)? ? }}
子組件改變父組件的state方式有很多種传于,可以在父組件設(shè)置一個通用函數(shù),類似:setParentState醉顽,通過子組件回調(diào)處理時沼溜,就可以更方便的統(tǒng)一處理:
//父組件state={? ? data:{}}setParentState=obj=>{this.setState(obj);}//子組件onClick=()=>{this.props.setParentState({ data:xxx });}
永遠(yuǎn)不要直接設(shè)置state的值:this.state.data = { a: 1 }。這個會導(dǎo)致幾個問題: 1:組件不會重新渲染 2:shouldComponentUpdate(nextProps, nextState) 函數(shù)里面 this.state的值是已經(jīng)改變了,和nextState的值相同游添。
舉個栗子:
// wrong
const { data } = this.state;
data.a = 1;? ? // 等價于this.state.data.a = 1;
this.setState({ data }); // shouldComponentUpdate里面觀察到 this.state 和nextState的值是相同的
// 此時函數(shù)里面性能相關(guān)的優(yōu)化是無效的
// correct? 需要用到當(dāng)前state值的寫法
this.setState(state => ({ data: {...state.data, a: 1} }))
如果想學(xué)習(xí)Java工程化系草、高性能及分布式、深入淺出唆涝。性能調(diào)優(yōu)找都、Spring,MyBatis廊酣,Netty源碼分析的朋友可以加我的Java高級程序員群:180705916能耻,群里有阿里大牛直播講解技術(shù),以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家亡驰。