Fork me on GitHub

《问题总结》分类列表拖拽排序后,接着修改下拉选项值,分类列表排序顺序重新回到原始状态

问题描述

在做企业frontpagesetting的时候,有个分类列表,需要支持对分类进行排序,但是在对分类排序之后,然后修改下拉选项值,排序列表重新回到了原始状态

问题分析

在组件生命周期getDerivedStateFromProps方法中,比对props, state中的list数据,因为displayType是可变的,所以会判断 R.symmetricDifference不为空,从而导致重新赋值state的list值,造成排序被还原

解决方法

思路:

知道上面是因为排序之后在修改下拉选项值之后,导致的列表顺序还原,displayType是可变的。基于这个原因,不使用list来判断,而是使用itemIds单个的id数组,这样不管顺序怎么排列,和displayType的值怎么修改都不会造成顺序还原。

代码实现:

1、生命周期函数getDerivedStateFromProps中比对id数组,并赋值给state的itemIds

static getDerivedStateFromProps(props, state) {
    if (
      !R.isEmpty(
        R.symmetricDifference(
          R.pluck(props.draggableId, props.dataSource),
          state.itemIds
        )
      )
    ) {
      return {
        itemIds: R.pluck(props.draggableId, props.dataSource)
      }
    }
    return null
  }

2、渲染数据列表时候 ,通过遍历itemIds,根据id从dataSource中获取并显示相关filed字段

{this.state.itemIds.map((id, index) => {
                    const item = R.find(R.propEq(draggableId, id), dataSource)
                    return (
                      <Draggable
                        draggableId={item[draggableId]}
                        index={index}
                        key={item[draggableId]}
                      >
                        {(provided2, snapshot2) => (
                          <div
                            className={styles.dndRow}
                            ref={provided2.innerRef}
                            {...provided2.draggableProps}
                            {...provided2.dragHandleProps}
                            style={{
                              ...this.getDraggableStyle(
                                snapshot2.isDragging,
                                provided2.draggableProps.style
                              ),
                              ...this.getColumnsStyle()
                            }}
                          >
                            <div>
                              <img src={dragHandleIcon} />
                            </div>
                            {this.renderDraggableContent(item)}
                          </div>
                        )}
                      </Draggable>
                    )
                  })}

3、监听排序函数,将排序后的itemIds传递到父组件中

onDragEnd = result => {
    const { destination, source } = result
    const { onOrderChange } = this.props
    // dropped outside the list
    if (R.isNil(destination)) return
    // dropped in same position
    if (
      R.allPass([R.eqProps('droppableId'), R.eqProps('index')])(
        destination,
        source
      )
    ) {
      return
    }
    this.setState(
      prevState => ({
        itemIds: this.reorder(
          prevState.itemIds,
          source.index,
          destination.index
        )
      }),
      () => {
        onOrderChange(this.state.itemIds)
      }
    )
  }

  reorder = (list, startIndex, endIndex) => {
    const newOrder = R.insert(
      endIndex,
      list[startIndex],
      R.remove(startIndex, 1, list)
    )
    return newOrder
  }

4、父组件监听到排序函数重新表单赋值

handleOrderChange = newOrder => {
    this.props.form.setFieldsValue({
      order: newOrder
    })
  }

<Form.Item>
                <DragAndDropTable
                  draggableId="corporateCategoryNo"
                  dataSource={corporateFrontpageCategoryList.toJS()}
                  columns={getCorporateCategoryColumns(
                    corporateFrontpageCategoryList,
                    form
                  )}
                  onOrderChange={this.handleOrderChange}
                />
              </Form.Item>
              {getFieldDecorator('order', {
                initialValue: corporateFrontpageCategoryList
                  .map(corpCat => corpCat.get('corporateCategoryNo'))
                  .toArray()
              })(<React.Fragment />)}

5、selector中处理接口返回的controlSetting数据,在corporateCategoryList中添加列表所需要的name字段

export const getCorporateFrontpageCategoryList = createSelector(
  getCorporateCategoryList,
  controlSettingSelector,
  (corporateCategoryList, controlSetting) => {
    if (R.isNil(corporateCategoryList) || R.isNil(controlSetting)) {
      return List()
    }
    return controlSetting
      .get('corporateCategoryList')
      .map(frontpageCorpCat =>
        frontpageCorpCat.set(
          'name',
          corporateCategoryList
            .find(
              (corpCat, key, iter) =>
                corpCat.get('corporateCategoryNo') ===
                frontpageCorpCat.get('corporateCategoryNo')
            )
            .get('name')
        )
      )
  }
)

6、在getCorporateCategoryColumns中使用displayType.${corporateCategoryNo}.displayType形式给select赋值

数据格式 displayType: {CORPCATE10034: {…}, CORPCATE10091: {…}, CORPCATE10093

{
      title: i18n.t('corporateFrontpageSetting.label.imgStyle'),
      dataIndex: 'corporateCategory.displayType',
      key: 'displayType',
      render: ({ corporateCategoryNo, displayType }: Object, index: number) => {
        return getFieldDecorator(
          `displayType.${corporateCategoryNo}.displayType`,
          {
            initialValue: displayType
          }
        )(
          <Select style={{ width: 100 }}>
            <Select.Option value={0}>
              {i18n.t('corporateFrontpageSetting.label.smallImgStyle')}
            </Select.Option>
            <Select.Option value={1}>
              {i18n.t('corporateFrontpageSetting.label.bigImgStyle')}
            </Select.Option>
          </Select>
        )
      }
    },
//删除列表行数据
{
      title: i18n.t('corporateFrontpageSetting.label.action'),
      dataIndex: 'corporateCategory.action',
      width: 100,
      key: 'action',
      render: (record: Object) => {
        const updatedFrontpageCorporateCategoryList = corporateCategoryList.filterNot(
          item => item.get('corporateCategoryNo') === record.corporateCategoryNo
        )
        return (
          <DelBtn
            updatedFrontpageCorporateCategoryList={
              updatedFrontpageCorporateCategoryList
            }
          />
        )
      }
    }

7、最后请求保存接口的时候对数据做处理

解构formValues中除了order和displayType的数据,处理并组装corporateCategoryList数据,最后拼装并返回接口请求所需格式
corporateHome: {articleIds: Array(4), displayCorporateCategory: "CORPCATE10077"}
corporateCategoryList: (6) [{…}, {…}, {…}, {…}, {…}, {…}]

patchFormValues = formValues => {
    return {
      ...R.omit(['order', 'displayType'])(formValues),
      corporateCategoryList: R.map(
        corporateCategoryNo => ({
          corporateCategoryNo,
          displayType: R.path(
            ['displayType', corporateCategoryNo, 'displayType'],
            formValues
          )
        }),
        R.path(['order'])(formValues)
      )
    }
  }

2020.3.4更新

测试的时候发现了个bug,在排序后接着删除一天分类数据,再保存页面数据失败。

  1. 原因
    排序后的order和删除后的order数据不一致导致
  2. 解决办法
    删除成功后重新设置order的数据
const handleConfirm = e => {
    message.loading({
      content: t('message.deleting'),
      key: 'API_REQUEST'
    })
    dispatch(
      updateControlSettingRequest({
        controlSetting: {
          corporateCategoryList: updatedFrontpageCorporateCategoryList.toJS()
        }
      })
    ).then(() => {
      setFieldsValue({
        order: getFieldValue('order').filter(item => {
          return item !== corporateCategoryNo
        })
      })
      message.success({
        content: t('message.deleteSuccess'),
        key: 'API_REQUEST'
      })
    })
  }

继续2020.3.4更新

上个问题的解决方式,有点粗暴,导致后续出现新的问题。
对分类排序后(未保存),接着添加新的分类,添加成功,但页面不能正常展示出来,需要刷新页面,原因还是和上面问题是同一个问题,添加成功后返回新数据没有动态更新form中order的值

解决办法是:
antd为我们提供了一个resetFields方法,这个方法可以重置form属性的initialValues初始值,所以只需要在添加分类成功后调用下这个resetFields方法即可,代码如下。同理解决上个问题,只要在删除成功后resetFields方法。

const handleConfirm = e => {
    message.loading({
      content: t('message.deleting'),
      key: 'API_REQUEST'
    })
    dispatch(
      updateControlSettingRequest({
        controlSetting: {
          corporateCategoryList: updatedFrontpageCorporateCategoryList.toJS()
        }
      })
    ).then(() => {
      resetFields(['order'])
      message.success({
        content: t('message.deleteSuccess'),
        key: 'API_REQUEST'
      })
    })
  }

解决参考

posted @ 2020-03-04 23:57  fozero  阅读(658)  评论(0编辑  收藏  举报