这篇先介绍了React组件之间传递值的几种类型及方式,最后还介绍了单页面应用开发过程中,遇到的兄弟组件之间的跳转及传值的两种方式。
React组件之间通信是单向的,数据只能由一方传到另一方。
组件之间的关系有:
- 父组件向子组件传递值
- 子组件向父组件传递值
- 兄弟组件之间通信
父组件向子组件传递值 (Parent=>Child)
父组件向一级子组件传递值,可以给子组件通过props
传递。自上而下进行传递。
class Parent extends Component {
render() {
return <Child data={data} />; // 向子组件传递data值
}
}
class Child extends Component {
render() {
return <p>{this.props.data}</p>; // 接收到父组件Parent传递来的data值
}
}
父组件向二级子组件传递值,可以通过...
展开props
属性,将父组件Parent的信息,简洁的传递给更深层级子组件。
class Child extends Component {
render() {
return <div>
<p>{this.props.data}</p>
<Child_Child {...this.props}/> {/* 将props解构,全部赋给二级子组件 */}
</div>
}
}
class Child_Child extends Component {
render() {
return <p>{this.props.data}</p>; // 接收到父组件Parent的值
}
}
注:通过这种方式,将父组件的值传递给向子组件,父组件的props与state改变,也会导致子组件的生命周期改变。
子组件向父组件传递值 (Child=>Parent)
子组件向父组件通讯,同样需要父组件向子组件传递props
,只是父组件传递的不是值,而是传递一个函数,这个函数的作用域为父组件。子组件调用这个函数,将子组件要传递的信息,作为参数,传递到父组件作用域中。父组件作用域中的该函数执行时,就能调用到这个参数了。
class Parent extends Component {
state = {
data: 'data'
}
getChildMessage (newData) {
this.setState({
data: newData
})
}
render() {
return <div>
<Child fn={(data) => this.getChildMessage(data)} />
//或者 <Child fn={this.getChildMessage} />
</div>
}
}
class Child extends Component {
componentDidMount() {
setTimeout(() => {
this.props. getChildMessage('child-data'); // 调用父组件传来的函数,将数据作为参数传过去
}, 1000);
}
render() {...}
}
兄弟组件之间通信 (ChildA=>ChildB)
对于没有直接关联的两个组件,ChildA和ChildB之间,他们之间唯一的联系就是,它们有相同的父组件Parent。
如果我们想由A向B通讯,可以先从A向Parent通讯,再由Parent向B通讯。
class Parent extends Component {
state = {
data: 'data'
}
getChildMessage (newData) {
this.setState({
data: newData
})
}
render() {
return <div>
<ChildA fn={(data) => this.getChildMessage(data)} /> {/* 父组件从子组件A获取到值 */}
<ChildB data={this.state.data} /> {/* Parent向子组件B传值 */}
</div>;
}
}
class ChildA extends Component {
componentDidMount() {
setTimeout(() => {
this.props.getChildMessage('child-data'); // 向Parent传值
}, 1000);
}
render() {...}
}
class ChildB extends Component {
render() {
return <p>{this.props.data}</p>; {/* 从Parent传来的值 */}
}
}
这个方法存在一个问题是,当Parent的state发生变化,会触发Parent以及所属的子组件的生命周期。
兄弟组件之间的通讯,有没有更好的方式?
观察者模式 (发布者-订阅者)
发布者发布事件,订阅者监听事件 并做出反应。
。。。
在实际开发单页面应用时,遇到页面之间的跳转以及参数传递举例
1. 路由。
通过React的路由库,实现页面跳转,和页面之间的参数传递。
将A页面里获取到的数据,在页面跳转到B时,传给B页面。
// Parent
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
import ChildA from './ChildA';
import ChildB from './ChildB';
const App = () => {
<Router>
<Switch>
<Route exact path="/" component={ChildA} />
<Route path="/result/:name" component={ChildB} />
</Switch>
</Router>
};
// ChildA
import { withRouter } from 'react-router-dom';
class ChildA extends Component {
...
this.props.history.push('/result/' + encodeURIComponent(name));
...
}
// this.props.history.push() 这个方法会触发组件页面之间的跳转
//ChildB
import { withRouter } from 'react-router-dom';
class ChildB extends Component {
render () {
return <div>{decodeURIComponent(this.props.match.params.name}</div>
}
}
// this.props.match.params.name 这个props上的值可以获得路由参数传来的值
路由的方式切换页面,存在的一个问题是,用户可以通过浏览器回退功能,退回到上一个页面。如果你的单页面应用不可回退到前一个页面,这个方法就不适合了。
2. 不用路由,通过父级的一个函数,改变变量的值,来控制显示A页面还是B页面。
这种方式可以组件内控制返回任意页面。不能通过浏览器回退返回到前一个页面。
class Parent extends Component {
state = {
page: 0,
name: null
}
go = (page, name) => {
const state = {
page
};
if (name) {
state.name = name;
}
this.setState(state);
}
render () {
const { page, name } = this.state;
// 父级根据参数确定跳转到哪
if (page) {
return <ChildB go={this.go} name={name} />;
}
return <ChildA go={this.go} name={name} />;
}
}
render(<Parent />, document.getElementById('wrap'));
class ChildA extends Component {
componentDidMount() {
// 判断父级传来的name是否有值,来做不同操作
if (this.props.name) { ... } else { ... }
}
...{
//如果要从A页面跳转到B页面。调用父级的go函数,传参 page=1,name=xxx
this.props.go(1, name);
}
}
class ChildB extends Compponent {
... {
this.props.go(0); // 重新返回A页面。“重新开始”的意思。page=0
}
}