一、父组件向子组件通信
React 数据流动是单向的,父组件向子组件的 通信也是最常见的方式。父组件通过 props 向子组件传递需要的信息
function EmailInput(props) {
return (
<label>
Email: <input value={props.email} />
</label>
);
}
const element = <EmailInput email="123124132@163.com" />;
ReactDOM.render(
element,
document.getElementById('root')
);
二、子组件向父组件通信
回调函数
function EmailInput(props) {
return (
<label>
Email: <input value={props.email} onChange={props.onChangeEmail} />
</label>
);
}
class App extends React.Component{constructor(props){super(props);
this.state = {email: ''}
}
onChangeEmail(e) => {console.log(e.target.value)
this.setState({email: e.target.value});
}
render(){let { email} = this.state;
return (<EmailInput email={ eamil} onChangeEmail={this.onChangeEmail} />;
)
}
}
ReactDOM.render(
<APP />,
document.getElementById('root')
);
三、跨级组件通信 Context
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言
不使用 Context 的情况
class App extends React.Component {render() {return <Toolbar theme="dark" />;}
}
function Toolbar(props) {
// Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,// 因为必须将这个值层层传递所有组件。return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {render() {return <Button theme={this.props.theme} />;
}
}
使用 Context 的情况
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。// 为当前的 theme 创建一个 context(“light”为默认值)。const ThemeContext = React.createContext('light');
class App extends React.Component {render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。// 无论多深,任何组件都能读取这个值。// 在这个例子中,我们将“dark”作为当前的值传递下去。return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。// React 会往上找到最近的 theme Provider,然后使用它的值。// 在这个例子中,当前的 theme 值为“dark”。static contextType = ThemeContext;
render() {return <Button theme={this.context} />;
}
}
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。使用 context 比较好的场景是真正意义上的全局信息且不会更改,例如界面主题、用户信息等
如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案
props 层层传递 user 和 avatarSize
<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<NavigationBar user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
将 Avatar 组件自身向下传递
function Page(props) {
const user = props.user;
const userLink = (<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
);
return <PageLayout userLink={userLink} />;
}
// 现在,我们有这样的组件:<Page user={user} avatarSize={avatarSize} />
// ... 渲染出 ...
<PageLayout userLink={...} />
// ... 渲染出 ...
<NavigationBar userLink={...} />
// ... 渲染出 ...
{props.userLink}
这种对组件组合减少了在你的应用中要传递的 props 数量,这在很多场景下会使得你的代码更加干净,使你对根组件有更多的把控。但是,这并不适用于每一个场景:这种将逻辑提升到组件树的更高层次来处理,会使得这些高层组件变得更复杂,并且会强行将低层组件适应这样的形式,这可能不会是你想要的。
而且你的组件并不限制于接收单个子组件。你可能会传递多个子组件,甚至会为这些子组件(children)封装多个单独的“接口(slots)”,React 元素本质就是对象(object),所以你可以把它们当作 props,像其他数据一样传递。这种方法可能使你想起别的库中“槽”(slot)的概念,但在 React 中没有“槽”这一概念的限制,你可以将任何东西作为 props 进行传递。
function Page(props) {
const user = props.user;
const content = <Feed user={user} />;
const topBar = (
<NavigationBar>
<Link href={user.permalink}>
<Avatar user={user} size={props.avatarSize} />
</Link>
</NavigationBar>
);
return (
<PageLayout
topBar={topBar}
content={content}
/>
);
}