React学习之组件通信,refs,key,ReactDOM

React学习之组件通信,refs,key,ReactDOM

1、组件间通信
父组件向子组件通信

React中规定了明确的单向数据流,利用props将数据从父组件传递到子组件,让父子组件进行通信,故父组件向子组件通信还是很容易实现的。引申一点,父组件怎么向孙子组件通信呢?可以利用props进行层层传递,使用ES6的…运算符可以用很简洁的方式把props传递给孙子组件。这里我们就不举例了。

子组件向父组件通信

子组件向父组件通信的方法也可以利用props,父组件利用props将一个方法传递给子组件,子组件回调这个方法的同时,将数据传递进去,是的父组件的相关方法得到回调,这个时候就可以进行子组件向父组件的通信了。下面看一个例子:

class Parent extends Component {
    handleMsg (msg) {
        console.log(msg)
    }
    render () {
        <div >
            <Child transferMsg = {msg => this.handleMsg(msg)}>
        </div>
    }
}
class Child extends Component {
    componentWillMount () {
        this.props.transferMsg('this is msg');
    }
    render () {
        <div>
            this is child
        </div>
    }
}
兄弟组件通信

兄弟组件通信的方法有好多种,比如利用父组件进行中转,数据先由child1传递给parent,再由parent传递给child2,这个方法显然耦合严重,传递次数过多,容易引发父组件不必要的生命周期回调,故不推荐使用。

我们可以利用观察者模式来解决这个问题。观察者模式采用发布订阅的方式,可以将消息发送者和接受者完美解耦。React中引入eventProxy模块,利用event.trigger()发布消息,eventProxy.on监听消息,下面看一个例子。

import eventProxy from '../eventProxy'

class Child1 extends React.Component {
  componentDidMount() {
    // 发布者,发出消息
    eventProxy.trigger('msg', 'child1 has been mounted');
  }
  render() {
    return (
      <div>child1</div>
    );
  }
}

class Child2 extends React.Component {
  componentDidMount() {
    // 订阅者,监听并接收消息
    eventProxy.on('msg', (msg) => {console.log('msg: ' + msg)});
  }
  
  render() {
    return (
      <div>child2</div>
    );
  } 
}
2、refs

我们在getRender()返回的JSX中,可以在标签中加入ref属性,然后通过refs.ref就可以访问到我们的Component了,例如。

class Parent extends React.Component {
  getRender() {
    <div>
      <Child ref = 'child' />
    </div>
  }
  
  componentDidMount() {
    // 通过refs可以拿到子元素,然后就可以访问到子元素的方法了
    let child = this.refs.child;
    child.test();
  }
}

class Child extends React.Component {
  test() {
    console.log("child method called by ref");
  }
}

要注意的是只有在组件进行了render之后才能获取到ref。

3、key

当我们的子组件是一个数组时,比如类似于Android中的ListView,一个列表中有很多样式一致的项,此时给每个项加上key这个属性就很有作用了。key可以标示当前项的唯一性。

对于数组,其内部包含长度不确定的子项。当组件state变化时,需要重新渲染组件。那么有个问题来了,React是更新组件,还是先销毁再新建组件呢。key就是用来解决这个问题的。如果前后两次key不变,则只需要更新,否则先销毁再更新。

对于子项的key,必须是唯一不重复的。并且尽量传不变的属性,千万不要传无意义的index或者随机值。这样才能尽量以更新的方式来重新渲染。React源码中判断更新方式的源码如下

function shouldUpdateReactComponent(prevElement, nextElement) {
  // 前后两次ReactElement中任何一个为null,则必须另一个为null才返回true。这种情况一般不会碰到
  var prevEmpty = prevElement === null || prevElement === false;
  var nextEmpty = nextElement === null || nextElement === false;
  if (prevEmpty || nextEmpty) {
    return prevEmpty === nextEmpty;
  }

  var prevType = typeof prevElement;
  var nextType = typeof nextElement;

  // React DOM diff算法
  if (prevType === 'string' || prevType === 'number') {
    // 如果前后两次为数字或者字符,则认为只需要update(处理文本元素),返回true
    return (nextType === 'string' || nextType === 'number');
  } else {
      // 如果前后两次为DOM元素或React元素,则必须type和key不变(key用于listView等组件,很多时候我们没有设置key,故只需type相同)才update,否则先unmount再重新mount。返回false
    return (
      nextType === 'object' &&
      prevElement.type === nextElement.type &&
      prevElement.key === nextElement.key
    );
  }
}
5、React DOM

React通过findDOMNode()可以找到组件实例对应的DOM节点,但需要注意的是,我们只能在render()之后,也就是componentDidMount()和componentDidUpdate()中调用。因为只有render后,DOM对象才生成了。

class example extends React.Component {
  componentDidMount() {
    // 只有render后才生成了DOM node,才能调用findDOMNode
    let dom = ReactDOM.findDOMNode(this);
  }
}

猜你喜欢

转载自blog.csdn.net/qq_34179086/article/details/88081643