使用 Route 配置的一个组件,当在浏览器中访问某一地址时,默认只会渲染该地址对应的组件,可能是在一个地方,也可能是多个地方(如果对该 Route 配置了多次),不仅如此,在同一地址下,我们也可以使用一些 ‘手段’ 渲染两个不同 location 下的组件。
Switch 组件会渲染 children 中的第一个匹配路径的组件,它还有一个 location 属性,该属性是可以动态改变的,利用这一特性,可以在某一地址下,把 Switch 组件的 location 属性手动改变以达到同一路径渲染多个不同组件的目的。比如有的情况下,需要弹出 Modal 对话框,而原来的组件保持不变,仍然渲染在页面中,这种情况下就适合使用 Switch 组件动态改变 location 来实现。
假定有这样一个场景,点击图片列表页面中的小图查看大图,大图使用 Modal 弹出的样式,在大图显示的时候,为了不使图片其它空白地方无填充内容,所以大图弹出的时候,使用半透明色使原有页面仍然可见。
定义样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var styles = {} styles.modal_root = { position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, background: 'rgba(0, 0, 0, 0.15)' } styles.modal_panel = { position: 'absolute', background: '#fff', top: 0, left: '10%', right: '10%', bottom: 0, border: '2px solid #444' }
|
上面定义了两个样式:
- modal_root : 弹出框布局样式
- modal_panel : 嵌套在弹出框中显示大图的布局样式
定义 Modal 弹出框
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| const IMAGES = [ {id: 0, title: '深兰花紫', color: 'DarkOrchid'}, {id: 1, title: '石灰绿', color: 'LimeGreen'}, {id: 2, title: '番茄色', color: 'Tomato'}, {id: 3, title: '#七八九', color: '#789'}, {id: 4, title: '赤红色', color: 'Crimson'} ] const Modal = ({match, history}) => { const image = IMAGES[parseInt(match.params.id, 10)] if (!image) { return null; } const back = (e) => { e.stopPropagation() history.goBack() } return ( <div onClick={back} style={styles.modal_root}> <div className='modal' style={styles.modal_panel}> <h1>{image.title}</h1> <Image color={image.color}/> <button type='button' onClick={back}>关闭</button> </div> </div> ) } const Image = ({color}) => <div style={{ width: '100%', height: '80%', background: color }}/>
|
全部图片使用 IMAGES 来存储,在 Modal 弹出的时候,根据 match.params.id 在 IMAGES 数组中查找当前需要显示大图的 IMAGE。每个 IMAGE 有 title 和 color 属性,使用 Image 组件来显示(背影颜色)图片。点击根布局或者关闭按钮,可以关闭 Modal。
定义图片列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const Gallery = () => { return ( <div> {IMAGES.map(i => ( <Link key={i.id} to={{pathname: `/img/${i.id}`, state: { modal: true }}}> <Thumbnail color={i.color}/><p>{i.title}</p> </Link> ))} </div> ) } const Thumbnail = ({color}) => <div style={{ width: 50, height: 50, background: color }}/>
|
使用一个 50 50 大小的 div 表示小图,以及图片颜色。使用 Link 指定小图被点击之后的跳转 location。其中 pathname 传递了图片的 id ,state 表示是否是 *Modal 弹出。
定义应用程序组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| class GalleryComponent extends React.Component { constructor(props) { super(props) this.previousLocation = this.props.location } componentWillUpdate(nextProps) { const {location} = this.props; if (nextProps.history.action !== 'POP') { this.previousLocation = this.props.location } } render() { const {location} = this.props const isModal = !!( location.state && location.state.modal && this.previousLocation !== location ) return ( <div> <Switch location={isModal? this.previousLocation: location}> <Route exact path='/' component={Gallery}/> </Switch> {isModal ? <Route path='/img/:id' component={Modal}/> : null} </div> ) } } var router = ( <Router> <Route component={GalleryComponent}/> </Router> );
|
在 componentWillUpdate 方法中,如果 action 不为 POP (不为 POP 就是 PUSH),就把当前的地址给保存到 this.previousLocation。然后在 render 方法中,如果是 isModal 弹出的话,则把 Switch 的 location 指定成 this.previousLocation,则 Switch 中的 Route 将被渲染,同时在外部一并渲染 Modal 组件。
整个流程如下:
一开始路径是 localhost:8888/ ,此时渲染的是 Gallery 组件,点击 Gallery 组件中的某一个图片,componentWillUpdate 方法被调用,此时 this.props.location.pathname 为 /,将该值保存到 this.previousLocation,render 方法被调用,此时 isModal 为 true,则 Switch 组件的 location 为 /,这个路径正好匹配 Gallery,同时 Modal 也会被渲染。