React入門,大神輕噴哈^_^
下面的代碼是創建在React 0.14.*版本的
今天在嘗試封裝React component的時候碰到了幾個問題,猜小白們學習React中可能會碰到,我就整理下但願能幫助到小白們。
Keyword: props children cloneElementhtml
React父子組件交流通常是用props傳遞,好比:react
const T = React.createClass({ render() { return <h1>{ this.props.text }</h1> } }); const B = React.createClass({ render() { return ( <header> <T text = "level A" /> <T text = "level B" /> </header> ) } }); ReactDOM.render( <B />, document.querySelector('#container'));
運行後天然是輸出下面兩行啦
level A
level Bgit
上面栗子中有兩個子組件包含在<header />中,這裏<header>是咱們熟悉的html標籤,可是假設換作自定義的組件,以下,若是控制< D />的子組件?
這時候就須要利用this.props的一個重要屬性了:children。github
const C = React.createClass({ render() { return ( <D> <h1>lever C</h1> <h1>lever D</h1> </D> ) } }); const D = React.createClass({ render() { return( <div> { this.props.children } </div> ) } }); ReactDOM.render( <C />, document.querySelector('#container'));
這裏this.props.children會得到傳入的兩個React element <h1>併成的數組,將其輸出到終端以下:(後邊會繼續介紹這個,先pass)api
重要的問題來了,那如何控制傳入的elements咧?總不能你給我什麼就呈現什麼吧,好比上面的栗子中我就想把傳入的元素通通變成紅色字體,怎麼作?數組
給外層div加個ref,如 X , 而後利用this.refs.X.style.color = '#f00'。這種作法只能勉強知足當前作法,很不靈活,一旦要修改的樣式子級沒法繼承就沒用了,因此不可取。學習
給外層div加個className如 cl ,接着在樣式文件中寫明.cl h1 的樣式。這種作法比上一種方案好一點,可是缺點是隻能應付樣式,而且須要在已知children的標籤類型才行。但假設需求就是給children的全部的文本類標籤添加紅色樣式,但不說明用的是<h1>,<h2>,<h3>仍是<span>等等標籤,那這種作法就須要在樣式文件中窮舉了,固然不可取。字體
是否能夠直接遍歷this.props.children進而修改屬性呢?this
<D> <img ref="img" key="img" src="./logo.png" wen="wen" /> </D>
舉上面栗子,輸出的this.props.children,展開後會發現:組件在React中是以一個對象的形式存在,包含了type, ref 和 Key 屬性,還有最重要的props屬性。
props包含了除前邊三個以外的寫在組件上的其餘屬性,包括默認屬性src / alt這些,還有自定義的如這裏的wen屬性。
!這裏注意的是props中不包含{ref,key},下面會說起到。spa
因而考慮經過直接對props進行修改屬性的方法來達到咱們的目的,修改D組件。
const D = React.createClass({ render() { let children = this.props.children.map( (o, i)=>{ o.props.style = { color: '#f00' }; return o; }); return ( <div> { children } </div> ) } });
運行後驚呆了,報錯了:
Uncaught TypeError: Can't add property style, object is not extensible
意思是說props屬性是不能擴展的,即不能修改。
後面繼續嘗試給o增刪屬性都是會報錯,說明這裏遍歷的每一個對象o都是read only的。
醉了。。。這還怎麼達到咱們的目的咧?
因而乎繼續回頭翻閱React官方文檔,瞄到了React.cloneElement,忽然意識到了什麼,沒錯,正面太鋒芒,咱們繞開它,直接修改對象不行,咱們就拷貝一個本身用。
ReactElement cloneElement( ReactElement element, [object props], [children ...] )
這裏的element是咱們要拷貝的原對象,在這裏是o。
props可選,當傳入個對象的時候是會和原對象的props進行合併,合併的方式是無則添加,有則覆蓋。
children就是前邊提到的props.children,用來加入子組件的,也是可選,這個栗子中暫時用不到。
因而,嘗試修改代碼以下:
const D = React.createClass({ render() { let children = this.props.children.map( (o, i)=>{ return React.cloneElement(o, { style: {color:'#f00'} }) }); return ( <div> { children } </div> ) } });
運行以後,成了!!!
固然擴展的話能夠根據判斷o.type或者其餘要求來相應的修改屬性,此處統一將全部的<h1>變爲紅色。
不過到這裏還沒結束,雖然效果有了,可是在console中仍是出現了警告:
Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of D
. It was passed a child from C.
這個好理解,就是在遍歷數組的時候須要給每一個元素添加Key屬性。不過問題來了,前邊說,key不算是Props的一員,即不能經過o.props.key=...來添加key。看React.cloneElement的參數列表中只有element, props和children,我又醉了,此次文檔也沒幫到什麼了。
因而乎,看看源碼cloneElement的源碼,不看不知道,一看想給文檔一巴掌,誤導我了!
是否是恍然大悟了?沒錯,雖然參數名是叫Props,不過實際上React是把ref/key這些屬性也當成Porps的一部分(報錯的信息裏邊也確實說"key" prop),只是在保存的時候會單獨拿出來,爲了和其餘屬性區分開。
在clone的過程當中,對props的遍歷會先單獨把ref和key拿出來判斷,而且不會保存在新的Props中,也就在輸出的時候看到的是分離的。
不過,既然clone的時候props參數包含着Key,那就容易了,修改一行代碼以下。
const D = React.createClass({ render() { console.log(React.cloneElement); // 這裏須要注意若是this.props.children只有一個元素,那將不是一個數組, // 因此仍是須要提早判斷類型,用Array.isArray(this.props.children) let children = this.props.children.map( (o, i)=>{ return React.cloneElement(o, { style: {color:'#f00'}, key: i }) }); return ( <div> { children } </div> ) } });
完成!!!
另外,React還提供了關於React.children的幾個有用的頂層API,也頗有用,這裏就不說了,具體的參考官方文檔哈。
https://facebook.github.io/react/docs/top-level-api.html#react.children