上节课我们介绍了用NavLink来代替Link编写路由链接,那么这节课我们来介绍一下关于NavLink的封装。

回顾

在开始介绍NavLink的封装之前,我们来对上节课的内容来做一个简单的回顾

  • NavLink可以自动根据点击状态追加className,默认active
  • NavLink可以根据activeClassName来指定追加的className

以上就是上节课的主要内容,那么接下来我们来说一下关于NavLink应该如何封装。

为什么要封装

我们先来回顾一下我们之前的代码,我们两个导航项,那么我们就要写两个NavLink,但是如果我们的导航项一多,那么是不是就要在App组件中一直添加NavLink?而且我们写NavLink的时候,每个NavLink之间的重复内容是不是特别多啊?唯一不同的就是展示内容和to属性。

那么这样的话,如果我们要写十几个导航项的话,那么我们就要写十几个NavLink,这样我们代码的优化空间是不是就特别大啊?所以说我们要对这一部分来做一个封装。

如何封装

其实封装也很简单,那么我们现在来做一个组件叫MyNavLink,这个组件呢我们在里面把所有重复的东西都封装好,然后我们在App中渲染这个组件的时候,只把不同的内容传进去是不是就好了?就像这样<MyNavLink to="/test">Test</MyNavLink>

那么我来问大家,这个MyNavLink组件是路由组件还是一般组件?

这个组件是不是我们自己写出来渲染的?而且to属性是不是也是我们自己传的?所以说这个组件是不是一般组件啊?

实现封装

既然我们已经有了封装的思路了,那么我们来实现一下吧:

// MyNavLink 组件
import { NavLink } from 'react-router-dom'

export default class MyNavLink extends Component {
  render() {
    const {to} = this.props;
    return (
      <NavLink className="list-group-item" to={to}>About</NavLink>
    );
  }
}
// App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        ...
            <div className="list-group">
              <MyNavLink to="/about" />
              <MyNavLink to="/home" />
            </div>
        ...
      </div>
    );
  }
}

我们把暂时不需要关注的代码先隐藏一下,我们来看MyNavLink组件,首先,我们既然要实现我们自己的一个对NavLink的二次封装,那么是不是要基于NavLink?那么我们就要导入NavLikn。因为我们说了,要通过props来传入to属性,那么我们是不是要在MyNavLink组件中来接收啊?那么MyNavLink组件中的to属性就不能再写死了。

然后我们再来看App组件,我们手动渲染MyNavLink组件,而且正常传入了to属性,那让我们来看一下效果吧:

image-20220114091802787

我们也能切换,但是有一个问题就是我们这两个导航栏为什么都是About呢?因为我们在MyNavLink组件中给写死了,现在我们有一个问题,我们该怎么实现动态渲染展示的内容?

当然我们也可以通过props传进去啊,但是我们来回顾一下NavLink人家什么处理的?我们现在渲染MyNavLink是不是自闭合标签啊?但是NavLink不是自闭合的啊,人家有开始标签,结束标签,还有标签体展示内容。那么我们怎么实现呢?我们学过接收props,但是我们是不是梗本没学过如何去接收标签体内容啊?

但是实际上呢,标签体内容也是一个特殊的props吗,我们来改一下代码:

export default class MyNavLink extends Component {
  render() {
    const {to} = this.props;
    console.log(this.props);
    return (
      <NavLink className="list-group-item" to={to}>About</NavLink>
    );
  }
}
// App 组件
export default class App extends Component {
  render() {
    return (
      <div>
        ...
            <div className="list-group">
              <MyNavLink to="/about">About</MyNavLink>
              <MyNavLink to="/home">Home</MyNavLink>
            </div>
        ...
      </div>
    );
  }
}

我们来看,我们在App组件中不用自闭合标签了,我们也像NavLink一样传一个标签体内容进去,然后我们去MyNavLink标签打印一下props看一下:

image-20220114093622134

我们可以看到控制台中输出的对象里面有一个属性叫做children,这个属性的值就是我们的标签体内容,那么这下好了。我们改一下代码实现我们预期的效果吧:

export default class MyNavLink extends Component {
  render() {
    const {to, children} = this.props;
    return (
      <NavLink className="list-group-item" to={to}>{children}</NavLink>
    )
  }
}

App组件中我们之前已经做了修改了,这里就不在赘述了,我们来看MyNavLink组件,我们从props中接到children然后作为NavLink的标签体内容,然后我们来看一下效果:

image-20220114094807997

这次展示就正常了,而且我们点击导航项切换也正常。但是大家有没有觉得这么写有些麻烦?我们开始标签,标签体,结束标签。这么长,那么如果我让他自闭和不写标签体的话行不行呢?我们来试一下:

export default class MyNavLink extends Component {
  render() {
    const {to, children} = this.props;
    return (
      <NavLink className="list-group-item" to={to} children={children} />
    )
  }
}

来看代码,这是什么意思?我们是不是直接传了children属性?既然标签体内容默认是children属性,那么我们直接给指定children属性不就得了嘛,那我们来看一下效果:

image-20220114095417885

展示也还是没问题,可是我们还是会觉得麻烦,因为我to写了一个属性,children又写了一个属性,如果我有是个属性要往里面传那还一个个去写吗?我们不是可以这么写吗:

export default class MyNavLink extends Component {
  render() {
    return (
      <NavLink className="list-group-item" {...this.props} />
    )
  }
}

这是不是props批量传入啊?而且连解构赋值都省了。

以上便是封装MyNavLink的主要内容。

总结

  • 标签体内容是一个特殊的props属性
  • 用批量传入props来减少代码冗余

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/wrap_navlink_component/