有没有一种简单的方法可以为列表实现列选择器?

Is there a simple way of implementing a column picker for a List?

我们将实现一个列选择器,目前我唯一的想法是实现一个包装列表的 ColumnPickableList。这还将包含一个复选框列表,使用户能够隐藏列。

但在我继续之前,我只是想知道我是否正在重新安装轮子,是否有更简单的方法来解决这个问题?

没有更简单的方法。您必须为此实现自己的列表组件

我正在跟进这件事,因为我正在努力完成这项工作。也许是因为我选择创建一个包装器来过滤要显示的 children。所以从技术上讲,这种方法没有实现自己的列表。

我做了一个天真的草稿,我希望它能奏效,但它无法 re-render children 即使它们在 parent 组件中 changed/filtered .

ColumnPickableList render() 函数中的 console.log(..) 确实打印了正确的 children/props,但 children 仍然不会 update/re-render。关于为什么的任何线索?这种做法是不是太天真了?

这是当前草稿:

ColumnPicker.js

import React, { PropTypes } from 'react';

import Checkbox from 'material-ui/Checkbox';

export default class ColumnPicker extends React.Component {

  constructor(props) {
    super(props);
    this.onCheck = this.onCheck.bind(this);
  }

  onCheck(column, isChecked) {
    return this.props.onCheckboxChanged(column, isChecked);
  }

  renderCheckbox(column, onCheck) {
    const disabled = (column.source === 'id');
    return (<Checkbox key={column.source} label={column.source.toUpperCase()} onCheck={(event, checked) => onCheck(column, checked)} defaultChecked disabled={disabled} />);
  }

  render() {
    const columns = this.props.columns || [];

    return (
      <div className="column-picker">
        {columns.map((column) => {
          return this.renderCheckbox(column, this.onCheck);
        })}
      </div>
    );
  }
}

ColumnPicker.propTypes = {
  columns: PropTypes.array,
  onCheckboxChanged: PropTypes.func,
};

ColumnPicker.defaultProps = {
  columns: [], // [{source: myField, checked: true} ...]
};

ColumnPickableList.js:

import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { List, Datagrid } from 'admin-on-rest';
import ColumnPicker from './ColumnPicker';

import { toggleColumnPickerStatusAction, initializeColumnPickerAction } from './actions';

export class ColumnPickableList extends React.Component {

  componentWillMount() {
    let columnSourceNames = [];
    if (this.props.children) {
      columnSourceNames = React.Children.map(this.props.children, (child) => {
        return ({ source: child.props.source, checked: true });
      });
    }
    const columnsDisplayed = columnSourceNames.filter((column) => column.source);
    this.props.initializeColumnPicker(this.props.resource, columnsDisplayed);
  }

  shouldComponentUpdate(nextProps) {
    const diff = nextProps.columnsDisplayed.filter((currentColumn) => {
      return !this.props.columnsDisplayed.some((prevColumn) => {
        return currentColumn.source === prevColumn.source && currentColumn.checked === prevColumn.checked;
      });
    });

    return diff.length > 0;
  }

  removeHiddenColumns(children) {
    return React.Children.map(children, (child) => {
      if (!child.props.source) {
        return child;
      }
      const column = this.props.columnsDisplayed.find((columnDisplayed) => {
        return columnDisplayed.source === child.props.source;
      });

      if (this.props.columnsDisplayed.length === 0 || (column && column.checked)) {
        return React.cloneElement(child);
      }
      return null;
    });
  }

  render() {
    const { children, ...rest } = this.props;
    const displayedChildren = this.removeHiddenColumns(children);
    console.log('Does it render? Rendering children', displayedChildren.map((child) => child.props.source));

    return (
      <div className="columnpickable-list">
        <ColumnPicker columns={this.props.columnsDisplayed} onCheckboxChanged={this.props.handleCheckboxChanged} />
        <List {...rest}>
          <Datagrid>
            {displayedChildren}
          </Datagrid>
        </List>
      </div>
    );
  }
}

ColumnPickableList.propTypes = {
  resource: PropTypes.string,
  columnsDisplayed: PropTypes.array,
  children: PropTypes.node,
  initializeColumnPicker: PropTypes.func,
  handleCheckboxChanged: PropTypes.func,
};

ColumnPickableList.defaultProps = {
  columnsDisplayed: [],
};


function mapStateToProps(state) {
  return {
    columnsDisplayed: state.columnsDisplayed || [],
  };
}

actions.js:

export const actions = {
  INIT_COLUMNPICKER: 'INIT_COLUMNPICKER',
  TOGGLE_COLUMNPICKER_STATUS: 'UPDATE_COLUMNPICKER_STATUS',
  UPDATE_COLUMNPICKER_STATUSES: 'UPDATE_COLUMNPICKER_STATUSES',
}


export function initializeColumnPickerAction(resource, columns) {
  return {
    type: actions.INIT_COLUMNPICKER,
    columns,
    meta: { resource },
  };
}


export function toggleColumnPickerStatusAction(column) {
  return {
    type: actions.TOGGLE_COLUMNPICKER_STATUS,
    column,
  };
}

reducers.js:

import { actions } from './actions';

function columnPickerReducer(state = [], action) {
  switch (action.type) {
    case actions.INIT_COLUMNPICKER: {
      console.log('Init columnopicker reducer');
      return action.columns;
    }
    case actions.TOGGLE_COLUMNPICKER_STATUS: {
      const columns = state.map((column) => {
        if (column.source === action.column.source) {
          return { ...column, checked: !column.checked };
        }
        return column;
      });
      return columns;
    }
    default:
      return state;
  }
}

export default columnPickerReducer;

parent 组件的示例片段:

...

<ColumnPickableList title="SillyStuff" {...props}>
  <TextField source="id" />
  <TextField source="NAME" />
  <TextField source="SILLY_NAME" />
  <TextField source="CHANGED_BY" />
  <DateField source="CHANGED_TS" showTime />
  <EditButton />
  <DeleteButton />
</ColumnPickableList>
...