React開發中遇到的問題總結

夢凡發表於2018-05-03

在使用React開發專案過程中,踩過很多坑,在踩坑填坑過程中也學到了不少知識。現就將之前遇到的問題總結下來,一來可以讓自己對這些知識點加深印象,二來也可以幫助更多的人在遇到同樣的問題時可以少走彎路。

元件名命名問題

類名首字母必須大寫,多個單片語合要用帕斯卡命名法(首字母大寫,其它單詞首字母也要大寫)

state更新問題

例如下面的例子中,結果是state沒更新;因為this.state.value是物件型別資料,和value指向的是同一個記憶體地址,引用的是同一個資料;對value的操作實際上就是操作原 this.state.value,所以value和this.state.value相同,對比下,沒有改變;所以在這裡的解決辦法就是:

var value=this.state.value.slice();複製一個this.state.value出來給value,這樣value改變了,就可以跟this.state.value作對比了。

getInitialState: function(){
    return {value: {foo: 'bar'}};
},
onClick: function(){
    var value = this.state.value; //應該這樣: var value = this.state.value.slice();
    value.foo += 'bar';
    this.setState({value: value});
} 
複製程式碼

this指向問題

componentDidMount() {
    QueryStore.on('sync', this.handleQuerySync);
    this.initializeWithProps();
}
複製程式碼

this.handleQuerySync的 this 並不是指向當前元件,指向的是QueryStore,解決辦法:(當然es6的箭頭函式也可以解決this指向問題)

......
constructor(props, context) {
    super(props, context);
    this.state = {};
    this.handleQuerySync = this.handleQuerySync.bind(this);
}
componentDidMount() {
    QueryStore.on('sync', this.handleQuerySync);
    this.initializeWithProps();
}
......
複製程式碼

render 返回格式

render 中 return的內容要麼是空,要麼就是一個節點,所以當有很多節點需要return時,需要用一個節點包裹起來。

js物件中的 key問題

看下面的程式碼,函式中傳入變數key

var BindingMixin = {
    handleChange: function(key){
        var that = this;
        return function(event){
        	var newState = {};
        	newState[key] = event.target.value;
        	that.setState(newState);
        	//that.setState({
        		//key: event.target.value
        	//})
        }	
    }
};
複製程式碼

上面註釋掉的程式碼是錯誤的,因為js物件中的 key值 不可以是變數

多次設定state問題

如下例子:

this.setState({
    data: datas
});
this.setState({
    data: datas
});
複製程式碼

如果兩次設定狀態,datas的值沒有改變,是不會觸發 render函式的; Javascrippt是基於事件驅動模型,假如在連續兩次 this.setState() 之後,React發現DOM沒有變更,此時React並不會觸發render方法

react 元件開發時遇到的問題

<Menu 
    vertical={true} 
    inverted={false}  
    placement={false} >
    <Menu.Item title={<span>Home</span>} \>
        <Menu.Item title="Home List1" />
        <Menu.Item title="Home List2" />
        <Menu.Item title={<a href="">Home List3</a>} /\>
    </Menu.Item>
   <Menu.Item title={<span><a href="">List</a></span>} /\>
</Menu>
複製程式碼

如上面的程式碼,如何把Menu 的屬性傳到子節點元件 Menu.Item 中去呢? 解決方法是:

renderList = (children, props) => {
    const extraProps = {
        inverted: props.inverted,
        vertical: props.vertical
    };

    return (
        React.Children.map(children, (child) => {
            return React.cloneElement(child, extraProps);
        })
    );
};
......
複製程式碼

外層元件渲染子節點元件時,克隆子節點,並把需要的屬性傳過去;這樣內層的子元件就可以拿到這些屬性;這裡傳過去的物件,不限於屬性,方法也可以傳過去;

內層元件如何複用外層元件的方法?

解決辦法: 上面說了,父元件渲染子元件時,用克隆子元件的方法,順帶把需要的屬性傳過去,其實還可以把父元件的 this 物件傳過去,這樣在子元件裡,就可以方便呼叫父元件的方法;如下程式碼:

//外層父元件
......
renderTreeNode = (child) => {    
    const defaultExpandAll = this.props.defaultExpandAll;
    const extraProps = {
        root: this,
        defaultExpandAll: defaultExpandAll
    };

    return React.cloneElement(child, extraProps);
};
......
    
//內層子元件這樣呼叫
......
newChildren = () => {
    let props = this.props;
    let children = this.props.children;

    return (
        <ul className={styles.subTree} >
            {React.Children.map(children, (item, index) => {
              return props.root.renderTreeNode(item, index);
            })}
        </ul>
    );
 };
 ......
複製程式碼

dangerouslySetInnerHTML

var Test = React.createClass({
getInitialState: function() {
    return {html: '<a href="#">這是一段html程式碼</a><a href="#">2</a><a href="#">3</a>};
},

render: function() {
  return (
       <div>{this.state.html}</div>
    );
  }
});

React.render(<Test />,  document.getElementById('example'));
複製程式碼

解析出來的還是這樣的一段程式碼

<a href="#">這是一段html程式碼</a><a href="#">2</a><a href="#">3</a>'<br><br>

因為react不會自動幫你解析你的html程式碼,不合時宜的使用 innerHTML 可能會導致 cross-site scripting (XSS) 攻擊;

var Test = React.createClass({
    getInitialState: function() {
        return {html: '<a href="#">這是一段html程式碼</a><a href="#">2</a><a href="#">3</a>};
    },

    render: function() {
        return (
            <div dangerouslySetInnerHTML={{__html: this.state.html}}></div>
        );
    }
});

React.render(<Test />,  document.getElementById('example')); 
複製程式碼

這麼做的意義在於,{__html:...} 背後的目的是表明它會被當成 "type/taint" 型別處理。 這種包裹物件,可以通過方法呼叫返回淨化後的資料,隨後這種標記過的資料可以被傳遞給 dangerouslySetInnerHTML

其它一些遇到的問題

  • 元件被解除安裝後,store裡的資料並不會被清空;必要時得在元件掛著時清空store的資料

  • 父元件監聽資料變化就可以了,不需要每個子元件自己又監聽一遍,這樣容易出現bug,而且效能也不好,可以通過props向子元件傳遞;

  • 關於列表key

遍歷列表時給每個子專案賦key時,不要直接給 index, 因為如果增加或者刪除子專案時,index 都會變,導致出問題; 也不可以用 index+ string,這樣因為如果增加或者刪除子專案時,所有子專案都會走更新流程,原因是index 在變; 所以在給子專案賦值時,要給唯一不變的數最好。

  • 關於store中的資料有變化,但是元件中的監聽函式沒有監聽到資料變化

這樣的問題出現過很多次了,不要鑽牛角尖,去鬱悶為啥資料有變化監聽不到呀,為啥呀!其實,資料變化了,監聽不到,本質原因就是沒有監聽;可能其他的程式碼解除安裝了監聽函式。

相關文章