React project actual combat renting app project (6) Rendering the listing list & axios optimization & encapsulating the top search bar & list search module conditional screening

foreword

1. Map house search module - obtain and render the listing list

1.1 Example diagram of listing list

insert image description here

1.2 Implementation steps

1、在单击事件中,获取小区下的所有房源数据
2、展示房源列表
3、渲染获取到的房源列表
4、调用地图 panBy() 方法,移动地图到中间位置。
5、监听地图 movestart 事件,在地图移动时隐藏房源列表。

1.3 Code example

Add the following code in src/pages/Map/index.js:

  // 创建小区覆盖物
  createRect(point, name, count, id) {
    
    
    // 创建覆盖物
    const label = new BMap.Label('', {
    
    
      position: point,
      offset: new BMap.Size(-50, -28)
    })

    // 给 label 对象添加一个唯一标识
    label.id = id

    // 设置房源覆盖物内容
    label.setContent(`
      <div class="${
      
      styles.rect}">
        <span class="${
      
      styles.housename}">${
      
      name}</span>
        <span class="${
      
      styles.housenum}">${
      
      count}套</span>
        <i class="${
      
      styles.arrow}"></i>
      </div>
    `)

    // 设置样式
    label.setStyle(labelStyle)

    // 添加单击事件
    label.addEventListener('click', e => {
    
    
      //调用获取房源列表方法
      this.getHousesList(id)

      // 获取当前被点击项
      const target = e.changedTouches[0]
      // 调用地图 panBy() 方法,移动地图到中间位置 
      this.map.panBy(
        window.innerWidth / 2 - target.clientX,
        (window.innerHeight - 330) / 2 - target.clientY
      )
    })

    // 添加覆盖物到地图中
    this.map.addOverlay(label)
  } 

How to get residential property data:

  // 获取小区房源数据
  async getHousesList(id) {
    
    
    const res = await axios.get(`http://localhost:8080/houses?cityId=${
      
      id}`)
    this.setState({
    
    
      housesList: res.data.body.list,

      // 展示房源列表
      isShowList: true
    })
  }

The method of rendering the housing list of the community:

  // 封装渲染房屋列表的方法
  renderHousesList() {
    
    
    return this.state.housesList.map(item => (
      <div className={
    
    styles.house} key={
    
    item.houseCode}>
        <div className={
    
    styles.imgWrap}>
          <img
            className={
    
    styles.img}
            src={
    
    `http://localhost:8080${
      
      item.houseImg}`}
            alt=""
          />
        </div>
        <div className={
    
    styles.content}>
          <h3 className={
    
    styles.title}>{
    
    item.title}</h3>
          <div className={
    
    styles.desc}>{
    
    item.desc}</div>
          <div>
            {
    
    /* ['近地铁', '随时看房'] */}
            {
    
    item.tags.map((tag, index) => {
    
    
              const tagClass = 'tag' + (index + 1)
              return (
                <span
                  className={
    
    [styles.tag, styles[tagClass]].join(' ')}
                  key={
    
    tag}
                >
                  {
    
    tag}
                </span>
              )
            })}
          </div>
          <div className={
    
    styles.price}>
            <span className={
    
    styles.priceNum}>{
    
    item.price}</span>/</div>
        </div>
      </div>
    ))
  }

Render the listing list in the render method, and control the display and hiding through isShowList:

render() {
    
    
    return (
      <div className={
    
    styles.map}>
        {
    
    /* 顶部导航栏组件 */}
        <NavHeader>地图找房</NavHeader>
        {
    
    /* 地图容器元素 */}
        <div id="container" className={
    
    styles.container} />

        {
    
    /* 房源列表 */}
        {
    
    /* 添加 styles.show 展示房屋列表 */}
        <div
          className={
    
    [
            styles.houseList,
            this.state.isShowList ? styles.show : ''
          ].join(' ')}
        >
          <div className={
    
    styles.titleWrap}>
            <h1 className={
    
    styles.listTitle}>房屋列表</h1>
            <Link className={
    
    styles.titleMore} to="/home/list">
              更多房源
            </Link>
          </div>

          <div className={
    
    styles.houseItems}>
            {
    
    /* 房屋结构 */}
            {
    
    this.renderHousesList()}
          </div>
        </div>
      </div>
    )
  }

Note: The relevant styles of the listing list are in index.module.css, so I won’t show them too much here

Finally, bind the map movement event in the initialization map method to hide the list of listings:

// 给地图绑定移动事件
map.addEventListener('movestart', () => {
    
    
  if (this.state.isShowList) {
    
    
    this.setState({
    
    
      //隐藏房源列表
      isShowList: false
    })
  }
})

2. Axios optimization

2.1 Problem overview

Every time the interface is requested, the same baseUrl needs to be written. For example: http://localhost:8080
is too cumbersome, and when our project is deployed online, it is difficult to replace

2.2 Configure production environment and development environment

Configuration steps:

1、在项目根目录中创建文件 .env.development
2、在该文件中添加环境变量
//环境变量约定REACT_APP开头
REACT_APP_URL =http://localhost:8080
3、重新启动脚手架,脚手架在运行的时候就会解析这个文件
4、在utils/url.js 中,创建BASE_URL 变量
5、导出BASE_URL

Code example:
Add the following code in src/utils/url.js:

// 配置baseURL
export const BASE_URL = process.env.REACT_APP_URL

最后,可以在其他组件导入使用:

import {
    
    BASE_URL} from '../../utils/url.js'

2.3 axios optimization

Implementation steps:

1、在utils/api.js 中,导入 axios和BASE_URL
2、调用 axios.create() 方法创建一个axios实例
3、给 create 方法,添加配置baseURL,值为 BASE_URL
4、导出API对象

Code example:
Add the following code in src/utils/api.js:

import axios from 'axios'
import {
    
     BASE_URL } from './url.js'

// 创建axios的配置文件,里面配置baseURL路径
const config = {
    
    
    baseURL: BASE_URL
}

// 根据create 方法来构建axios对象
const instance = axios.create(config)

export {
    
     instance }

3. Encapsulate the top search navigation bar

Implementation steps:

1、在components 目录中创建组件 SearchHeader/index.js
2、把之前写过的结构拷贝到这个文件中
3、然后把跟首页相关的数据去掉,标题,城市名称
4、通过props来进行传递

Code example:
Add the following code in src/components/SearchHeader/index.js:

import {
    
     Flex } from 'antd-mobile';
import React from 'react'
import {
    
    withRouter} from 'react-router-dom'
import './index.scss'
import PropTypes from 'prop-types'

function SearchHeader({
     
      history, cityName, className}) {
    
    
    return (
        <Flex className={
    
    ['search-box', className || ''].join(' ')}>
            {
    
    /* 左侧白色区域 */}
            <Flex className="search">
                {
    
    /* 位置 */}
                <div className="location" onClick={
    
    () => history.push('/citylist')}>
                    <span className="name">{
    
    cityName}</span>
                    <i className="iconfont icon-arrow" />
                </div>

                {
    
    /* 搜索表单 */}
                <div className="form" onClick={
    
    () => history.push('/search')}>
                    <i className="iconfont icon-seach" />
                    <span className="text">请输入小区或地址</span>
                </div>
            </Flex>
            {
    
    /* 右侧地图图标 */}
            <i className="iconfont icon-map" onClick={
    
    () => history.push('/map')} />
        </Flex>
    )
}
// 设置校验
SearchHeader.propTypes = {
    
    
    cityName: PropTypes.string.isRequired
}
export default withRouter(SearchHeader)

4. List house search module - import the top navigation bar component

Implementation steps:

1、在找房页面 SearchHeader 组件基础上,调整结构(添加返回icon等)。
2、给 SearchHeader 组件传递 className 属性,来调整组件样式,让其适应找房页面效果。

Code example:
Then import the top search bar component in src/pages/Houselist/index.js:

import React from 'react'
import {
    
     Flex } from 'antd-mobile'

// 导入搜索导航栏组件
import SearchHeader from '../../components/SearchHeader'

// 导入样式
import styles from './index.module.css'

// 获取当前定位城市信息
const {
    
     label } = JSON.parse(localStorage.getItem('hkzf_city'))

export default class HouseList extends React.Component {
    
    
  render() {
    
    
    return (
      <div>
        <Flex className={
    
    styles.header}>
          <i
            className="iconfont icon-back"
            onClick={
    
    () => this.props.history.go(-1)}
          />
          <SearchHeader cityName={
    
    label} className={
    
    styles.searchHeader} />
        </Flex>
      </div>
    )
  }
}

Finally, create index.module.css in the current directory and set the corresponding style

5. List house search module - conditional screening (Part 1)

5.1 Conditional filtering effect diagram

insert image description here

5.2 Function Analysis

Conditional filtering needs to have the following functions:

1、点击标题菜单,展开条件筛选对话框,被点击的标题高亮
2、点击取消按钮或空白区域,隐藏对话框,取消标题高亮
3、选择筛选条件后,点击确定按钮,隐藏对话框,当前标题高亮
4、打开对话框时,如果有选择的条件,那么默认显示已选择的条件
5、打开对话框已经隐藏对话框有动画效果
6、标题框具有吸顶功能

To achieve the above functions, it is necessary to encapsulate four components for division of labor and cooperation, which are:

1、父组件:Filter
2、子组件:FilterTitle 标题菜单组件
3、子组件:FilterPicker 前三个菜单对应的内容组件
4、子组件:FilterMore 最后一个菜单对应的内容组件

5.3 Implementation of the FilterTitle component of the title menu component

Implementation ideas:

1、根据标题菜单数据,渲染标题列表
2、标题可以被点击
3、标题点击时高亮,有筛选条件选中时高亮
4、由父组件Filter提供高亮状态,子组件通过props接受状态来实现高亮
5、原则是单一数据源,只由父组件Filter提供和修改状态,其他组件只负责接收状态

Implementation steps:

1、在src/pages/Houselist目录下,新建components文件夹
2、在components目录下新建Filter,FilterTitle,FilterPicker,FilterMore组件
3、在FilterTitle组件中通过props接收高亮状态对象 titleSelectedStatus
4、遍历titleList数组,渲染标题列表
5、判断高亮对象中当前标题是否高亮,如果是,添加高亮类
6、给标题项绑定单击事件,在事件中调用父组件传过来的方法 onClick
7、将当前标题type,通过onClick的参数,传递给父组件
8、父组件中接收到当前type,修改标题的选中状态为true

Code example:
First, add the following code in src/pages/HouseList/components/Filter/index.js:

// 标题高亮状态
// true 表示高亮; false 表示不高亮
const titleSelectedStatus = {
    
    
  area: false,
  mode: false,
  price: false,
  more: false
}

export default class Filter extends Component {
    
    
  state = {
    
    
    titleSelectedStatus
  }

  // 点击标题菜单实现高亮
  // 父组件中接收到当前type,修改标题的选中状态为true
  onTitleClick = type => {
    
    
    this.setState(prevState => {
    
    
      return {
    
    
        titleSelectedStatus: {
    
    
          // 获取当前对象中所有属性的值
          ...prevState.titleSelectedStatus,
          [type]: true
        }
      }
    })
  }

  render() {
    
    
    const {
    
     titleSelectedStatus } = this.state

    return (
      <div className={
    
    styles.root}>
        <div className={
    
    styles.content}>
          {
    
    /* 标题栏 */}
          <FilterTitle
            titleSelectedStatus={
    
    titleSelectedStatus}
            onClick={
    
    this.onTitleClick}
          />
        </div>
      </div>
    )
  }
}

Then, add the following code in src/pages/HouseList/components/FilterTitle/index.js:

// 条件筛选栏标题数组:
const titleList = [
  {
    
     title: '区域', type: 'area' },
  {
    
     title: '方式', type: 'mode' },
  {
    
     title: '租金', type: 'price' },
  {
    
     title: '筛选', type: 'more' }
]

export default function FilterTitle({
     
      titleSelectedStatus, onClick }) {
    
    
  return (
    <Flex align="center" className={
    
    styles.root}>
      {
    
    titleList.map(item => {
    
    
        const isSelected = titleSelectedStatus[item.type]
        return (
          <Flex.Item key={
    
    item.type} onClick={
    
    () => onClick(item.type)}>
            {
    
    /* 选中类名: selected */}
            <span
              className={
    
    [
                styles.dropdown,
                // 判断当前的标题是否是选中状态,如果是,设置选中的样式
                isSelected ? styles.selected : ''
              ].join(' ')}
            >
              <span>{
    
    item.title}</span>
              <i className="iconfont icon-arrow" />
            </span>
          </Flex.Item>
        )
      })}
    </Flex>
  )
}

Summarize

Due to limited space, other components of conditional filtering will be reserved for the next note introduction

Guess you like

Origin blog.csdn.net/qq_40652101/article/details/128110581