react 省市区下拉组件的思考

import React, { PureComponent } from 'react';
import { Select, Spin } from 'antd';
import { connect } from 'dva';
import styles from './GeographicView.less';

const { Option } = Select;

const nullSlectItem = {
  label: '',
  key: '',
};

@connect(({ geographic }) => {
  const { province, isLoading, city, area } = geographic;
  return {
    province,
    city,
    area,
    isLoading,
  };
})
class GeographicView extends PureComponent {
  componentDidMount = () => {
    const { dispatch } = this.props;
    dispatch({
      type: 'geographic/fetchProvince',
    });
  };

  componentDidUpdate(props) {
    const { dispatch, value } = this.props;

    if (!props.value && !!value && !!value.province) {
      dispatch({
        type: 'geographic/fetchCity',
        payload: value.province.key,
      });
    }
  }

  getProvinceOption() {
    const { province } = this.props;
    return this.getOption(province);
  }

  getCityOption = () => {
    const { city } = this.props;
    return this.getOption(city);
  };
  
  getAreaOption = () => {
    const { city } = this.props;
    return this.getOption(city);
  };

  getOption = list => {
    if (!list || list.length < 1) {
      return (
        <Option key={0} value={0}>
          没有找到选项
        </Option>
      );
    }
    return list.map(item => (
      <Option key={item.id} value={item.id}>
        {item.name}
      </Option>
    ));
  };

  selectProvinceItem = item => {
    const { dispatch, onChange } = this.props;
    dispatch({
      type: 'geographic/fetchProvince',
      payload: item.key,
    });
    onChange({
      province: item,
      city: nullSlectItem,
      area: nullSlectItem,
    });
  };

  selectCityItem = item => {
    const { dispatch, onChange, value } = this.props;
    dispatch({
      type: 'geographic/fetchCity',
      payload: item.key,
    });
    onChange({
      province: value.province,
      city: item,
      area: nullSlectItem,
    });
  };

  selectAreaItem = item => {
    const { value, onChange } = this.props;
    onChange({
      province: value.province,
      city: value.city,
      area: item,
    });
  };

  conversionObject() {
    const { value } = this.props;
    if (!value) {
      return {
        province: nullSlectItem,
        city: nullSlectItem,
        area: nullSlectItem,
      };
    }
    const { province, city, area } = value;
    return {
      province: province || nullSlectItem,
      city: city || nullSlectItem,
      area: area || nullSlectItem,
    };
  }

  render() {
    const { province, city, area } = this.conversionObject();
    const { isLoading } = this.props;
    return (
      <Spin spinning={isLoading} wrapperClassName={styles.row}>
        <Select
          className={styles.item}
          value={province}
          labelInValue
          showSearch
          onSelect={this.selectProvinceItem}
        >
          {this.getProvinceOption()}
        </Select>
        <Select
          className={styles.item}
          value={city}
          labelInValue
          showSearch
          onSelect={this.selectCityItem}
        >
          {this.getCityOption()}
        </Select>
        <Select
          className={styles.item}
          value={area}
          labelInValue
          showSearch
          onSelect={this.selectAreaItem}
        >
          {this.getAreaOption()}
        </Select>
      </Spin>
    );
  }
}

export default GeographicView;

先上代码, 上面一段是 GeographicView.js

import { queryProvince, queryCity } from '@/services/geographic';

export default {
  namespace: 'geographic',

  state: {
    province: [],
    city: [],
    area: [],
    isLoading: false,
  },

  effects: {
    *fetchProvince(_, { call, put }) {
      yield put({
        type: 'changeLoading',
        payload: true,
      });
      const response = yield call(queryProvince);
      yield put({
        type: 'setProvince',
        payload: response,
      });
      yield put({
        type: 'changeLoading',
        payload: false,
      });
    },
    *fetchCity({ payload }, { call, put }) {
      yield put({
        type: 'changeLoading',
        payload: true,
      });
      const response = yield call(queryCity, payload);
      yield put({
        type: 'setCity',
        payload: response,
      });
      yield put({
        type: 'changeLoading',
        payload: false,
      });
    },
    *fetchArea({ payload }, { call, put }) {
      yield put({
        type: 'changeLoading',
        payload: true,
      });
      const response = yield call(queryCity, payload);
      yield put({
        type: 'setArea',
        payload: response,
      });
      yield put({
        type: 'changeLoading',
        payload: false,
      });
    },
  },

  reducers: {
    setProvince(state, action) {
      return {
        ...state,
        province: action.payload,
      };
    },
    setCity(state, action) {
      return {
        ...state,
        city: action.payload,
      };
    },
    setArea(state, action) {
      return {
        ...state,
        area: action.payload,
      };
    },
    changeLoading(state, action) {
      return {
        ...state,
        isLoading: action.payload,
      };
    },
  },
};

上面是models数据, 基于dva(redux以及saga的集成就不再详述了)

view中的部分, 关于下面一行代码的思考。

const { province, city, area } = this.conversionObject();

这里的this调用

conversionObject() {
    const { value } = this.props;
    if (!value) {
      return {
        province: nullSlectItem,
        city: nullSlectItem,
        area: nullSlectItem,
      };
    }
    const { province, city, area } = value;
    return {
      province: province || nullSlectItem,
      city: city || nullSlectItem,
      area: area || nullSlectItem,
    };
  }

该函数, 从而获取到了value的值(注意,这里的value是redux中通过models传递的 state里面的值哦(我也是刚发现还可以这么玩)

但是如果你在函数体内部输出this.props的话其实是输出为空的。 而在 render内调用则使用它的上下文也就是 class,因此能取到 props。

讲的很粗糙,还是仔细研究下代码吧,这种写法 可以很方便的封装一部分重复代码,减少重复的工作量(虽然对于新手来说增加了理解成本,但是用习惯以后,发现还真是因吹斯听呢)

原文地址:https://www.cnblogs.com/aleafo/p/10728425.html