前端笔记本前端

React组件之间的跳转及通信(传值)

2018-02-11  本文已影响49人  春困秋乏冬眠夏打盹

这篇先介绍了React组件之间传递值的几种类型及方式,最后还介绍了单页面应用开发过程中,遇到的兄弟组件之间的跳转及传值的两种方式。

React组件之间通信是单向的,数据只能由一方传到另一方。

组件之间的关系有:

  1. 父组件向子组件传递值
  2. 子组件向父组件传递值
  3. 兄弟组件之间通信

父组件向子组件传递值 (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
    }
}
上一篇 下一篇

猜你喜欢

热点阅读