## 父向子
### Props
React数据流动是单向的,父组件向子组件通信也是最常见的;父组件通过props向子组件传递需要的信息 Child.jsx
```
import React from 'react';
import PropTypes from 'prop-types';
export default function Child({ name }) {
return <h1>Hello, {name}</h1>;
}
Child.propTypes = {
name: PropTypes.string.isRequired,
};
```
```
import React, { Component } from 'react';
import Child from './Child';
class Parent extends Component {
render() {
return (
<div>
<Child name="Sara" />
</div>
);
}
}
export default Parent;
```
### Refs
Refs 是使用`React.createRef()`创建的,并通过`ref`属性附加到 React 元素。在构造组件时,通常将 Refs 分配给实例属性,以便可以在整个组件中引用它们。
* 当`ref`属性用于 HTML 元素时,构造函数中使用`React.createRef()`创建的`ref`接收底层 DOM 元素作为其`current`属性。
* 当`ref`属性用于自定义 class 组件时,`ref`对象接收组件的挂载实例作为其`current`属性。
* **你不能在函数组件上使用`ref`属性**,因为他们没有实例。
```
class Child extends React.Component {
myFunc() {
return "hello"
}
}
class Parent extends React.Component {
componentDidMount() {
var x = this.foo.myFunc() // x is now 'hello'
}
render() {
return (
<Child
ref={foo => {
this.foo = foo
}}
/>
)
}
}
```
## 子向父
### 利用回调函数
子组件通过调用父组件传来的回调函数,从而将数据传给父组件。
```
const Child = ({ onClick }) => {
<div onClick={() => onClick('zach')}>Click Me</div>
}
class Parent extends React.Component {
handleClick = (data) => {
console.log("Parent received value from child: " + data)
}
render() {
return (
<Child onClick={this.handleClick} />
)
}
}
```
###. 利用自定义事件机制
这种方法其实跟react本身没有关系,我们利用的是原生dom元素的事件冒泡机制。
```
class Parent extends React.Component {
render() {
return (
<div onClick={this.handleClick}>
<Child />
</div>
);
}
handleClick = () => {
console.log('clicked')
}
}
function Child {
return (
<button>Click</button>
);
}
```
## 兄弟组件
### 通过共同父组件
```
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {count: 0}
}
setCount = () => {
this.setState({count: this.state.count + 1})
}
render() {
return (
<div>
<SiblingA
count={this.state.count}
/>
<SiblingB
onClick={this.setCount}
/>
</div>
);
}
}
```
## 跨级组件、无相关关系的组件
### Context
```
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
```
简单的解析一下:
1. `React.createContext`创建了一个Context对象,假如某个组件订阅了这个对象,当react去渲染这个组件时,会从离这个组件最近的一个`Provider`组件中读取当前的context值
2. `Context.Provider`: 每一个Context对象都有一个`Provider`属性,这个属性是一个react组件。在Provider组件以内的所有组件都可以通过它订阅context值的变动。具体来说,Provider组件有一个叫`value`的prop传递给所有内部组件,每当`value`的值发生变化时,Provider内部的组件都会根据新value值重新渲染
3. 那内部的组件该怎么使用这个context对象里的东西呢?
a. 假如内部组件是用class声明的有状态组件:我们可以把Context对象赋值给这个类的属性`contextType`,如上面所示的ThemedButton组件
```
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
const value = this.context
return <Button theme={value} />;
}
}
```
b. 假如内部组件是用function创建的无状态组件:我们可以使用`Context.Consumer`,这也是Context对象直接提供给我们的组件,这个组件接受一个函数作为自己的child,这个函数的入参就是context的value,并返回一个react组件。可以将上面的ThemedButton改写下:
```
function ThemedButton {
return (
<ThemeContext.Consumer>
{value => <Button theme={value} />}
</ThemeContext.Consumer>
)
}
```
最后提一句,context对于解决react组件层级很深的props传递很有效,但也不应该被滥用。只有像theme、language等这种全局属性(很多组件都有可能依赖它们)时,才考虑用context。如果只是单纯为了解决层级很深的props传递,可以直接用[component composition](https://link.segmentfault.com/?enc=ocYc5MWTeXGADTzSENuxSA%3D%3D.JprVlKXFBTs9046VNwppqePK8AS5HvdkXahZWOTq2Pq2USAJ5yxbkolmLl63feKpbv%2FLKNExalJhFfbtJHtNZg%3D%3D)
### Redux
[React中组件通信的几种方式](https://juejin.cn/post/6844903520500449288)
[30分钟精通十种React组件之间通信的方法](https://segmentfault.com/a/1190000023585646)