React开发中遇到的问题总结

在使用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中的数据有变化,但是组件中的监听函数没有监听到数据变化

这样的问题出现过很多次了,不要钻牛角尖,去郁闷为啥数据有变化监听不到呀,为啥呀!其实,数据变化了,监听不到,本质原因就是没有监听;可能其他的代码卸载了监听函数。