## 父向子
### 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)