React 入门:走马(博客)观花(React 代码)

前言

这里是真正开始学习 React 的第一篇文章,在这篇文章里主要是把两周时间里用 React 从无到有搭建一个个人博客中所涉及到的一些常用代码块。这里入门的每一篇,也算是自己的处女作了。

首先说明:博客基本 ant.design ui 库进行开发。代码中使用的组件基本都来自于它。所以你可以有选择的忽略这些,只关注重点。

代码块专场

下面我就以在开发博客为主线,把实现一个博客涉及到 React 的常用代码和实现一一记录。

路由

第一要介绍的肯定是路由了:

这里我使用了多级路由(两级)代码如下:

<HashRouter>
  <Switch>
    <Route exact path="/user/login" component={Login} />
    <Route exact path="/user/register" component={Register} />
    <Route path="/admin" component={Admin} />
    <Route path="/" component={Main} />
  </Switch>
</HashRouter>

上面的头两个没有使用多级路由,后面再作优化,下面两个分别是后面一级路由,和前端一级路由,并且分别对应着两个不同的布局组件 Admin 和 Main。

在这里有一点需要注意:一级路由不要使用 exact 精确匹配路由,这样二级就不可能匹配得到了。

下面我们先来看看前端博客的结构组件 Main 中的部分代码:

<Row gutter={16} type="flex" justify="center">
  <Col { ...(this.props.location.pathname.match('detail') ? oneLayout.left : doubleLayout.left ) }>
    <ZSide />
  </Col>
  <Col { ...(this.props.location.pathname.match('detail') ? oneLayout.right : doubleLayout.right) }>
    <Route exact path="/" component={Home} />
    <Route path="/detail/:id" component={PostDetail} />
    <Route key={this.props.location.pathname} path="/category/:categoryAlias/" component={Home} />
  </Col>
</Row>

重点:

  1. this.props.location.pathname.match('detail') 通过匹配当前路由根据不同的页面给 Col 组件配置不同的参数
  2. 第二个 <Col> 组件中的 Route 路由,这里就是二级路由了。并且不同路由共用了同一个组件。

对于路由这里有几点需要注意:

  • 如果二级路由中没有子路由,此时我们可以使用 exact 属性。
  • 最后一个路由添加了唯一的 key 属性,这个是为了解决多路由共用同一个组件,页面不更新的问题,即组件实现共用实例的问题。

路由说完了,现在就来说说页面中的一些逻辑实现的代码。

同样的后台的结构组件 Admin 二级路由代码如下:

<Content style={{ margin: '24px 16px 0', overflow: 'initial' }}>
  <div style={{ padding: 24, background: '#fff' }}>
    {
      this.state.user.isAdmin ? 
      <>
        <Route exact path="/admin/post" component={Home} />
        <Route exact path="/admin/category" component={Category} />
        <Route exact path="/admin/edit/:id?" component={Edit} />
      </> : <Redirect to="/" />
    }
  </div>
</Content>

重点:

  1. 这里做了路由的鉴权,登录了才可以访问后面路由,如果没权限,则通过 <Redirect to="/" /> 跳转到前端首页。
  2. <></> 这个是空标签,用来包裹多个同级元素,因为表达式中的元素必需要有一个根元素,用这个空标签的好处就是不会产生额外的标签,也就是减少代码的嵌套。
  3. 与之对应的还有 React 对象为我们提供的 <React.Fragment></React.Fragment> 使用跟空标签差不多,只不过它是用在组件的最外层。即作为 return() 方法的根元素。

数据的处理(状态)

在组件内可以直接在构造函数中定义默认值

class Home extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      dataList: {
        data: [],
        pageSize: 15,
        pageNode: 1,
        total: 0
      },
      isLoading: false
    }
  }
}

重点:

  1. 如果想在构造函数中使用 this,那么你必需得在使用 this 之前或者构造函数内首行调用一次 super() 方法。
  2. 关于 state,除了在构造函数中使用 this.state = {} 的方式来直接给 state 赋值外,其它地方只能通过 this.setState({}) 的方式来修改数据,比如:
const response = await fetchPosts(pageOption)
this.setState({
  dataList: {
    data: response.data,
    pageSize: response.pageSize,
    pageNo: response.pageNo,
    total: response.total
  },
  isLoading: false
})

一般地我们可以在 componentDidMount 函数中调用请求数据的接口。

在render 函数中

如果需要判断才知道该显示哪一个组件的,那么可以使用下面这种试进行逻辑判断:

render() {
  return (
    this.state.isLoading ? (<div style={{ textAlign: "center" }}><Spin size="large" tip="为您全速加载中..." /></div>) :
      (<ZList fetchPosts={this.fetchPosts} dataList={this.state.dataList} />)
  )
}

重点:

  1. render 方法属于类组件,函数组件没有。
  2. 判断的写法跟三元表达式类似。不过代码中的括号不是必要的,可有可无,加了可以让代码结构更加清楚已懂而已。
  3. render 方法中的 return 后面是括号。

函数组件?类组件?

何时使用函数组件,何时使用类组件?无状态时可使用函数组件,比如:

import React from 'react'
import { Layout, Icon } from 'antd';
const { Footer } = Layout;
function ZFooter() {
  return (
    <Footer style={{ textAlign: 'center' }}>
    <p><a style={{marginRight:"24px"}} target="_blank" rel="noopener noreferrer" href="https://github.com/zhaoxixiong"><Icon type="github" style={{ fontSize: 24 }} /></a> ©2019 Created by 朝夕熊,PowerBy  React | ant.design</p>
    </Footer>
  )
}
export default ZFooter

因为网页的底部组件不包含任何状态(数据),所以我们直接渲染就行。

重点:

  1. 函数式组件的写法一个函数,函数可传参数(props),然后里面就只有一个 return () 来返回 HTML 代码,说白了就相当于静态页面。
  2. 在 React 中行间样式的书写格式为:style={{ }}

this 指向问题

<Search placeholder="搜你所想,万一有呢" onSearch={value => this.onSearch(value)}/>

比如在页面中有一个搜索组件,点击时调用 onSearch(),如果onSearch() 方法中有使用到 this ,此时 this 的指向就不是如你所愿了。

onSearch(value) {
   this.props.history.push({ pathname: '/', query: { s: value } })
}

如果不作相应的处理,this 会为 undefined 。那么要怎么解决 this 指向问题的?

第一种方法就像上面的那样外层套个箭头函数。

第二种方法就是通过 bind() 方法给函数绑定 this。

<Search placeholder="搜你所想,万一有呢" onSearch={this.onSearch.bind(this)}/>

非路由组件获取路由信息

这种场景也不少,也就是说比如首页的路由 / 对应的是 Main 组件,但 Main 组件中调用了其它组件,比如:ZSide 组件。由于没有路由对应 ZSide 组件所以在 ZSide 组件内的 props 属性就不会有路由的相关信息。此时我们需要在 ZSide 组件中使用 withRouter 进行包裹。

import React from 'react'
import { withRouter } from 'react-router-dom'
class ZSide extends React.Component {
  ......
}
export default withRouter(ZSide)

此时你就可以在 props 上调用路由的相关属性和方法了。