Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

最近的一些编程实践-react列表篇 #23

Open
Jeffersondyj opened this issue May 25, 2019 · 0 comments
Open

最近的一些编程实践-react列表篇 #23

Jeffersondyj opened this issue May 25, 2019 · 0 comments

Comments

@Jeffersondyj
Copy link
Owner

是的,列表篇,前面的兄弟篇—表单篇在此

本篇可能没有上面formik讲那么多api,主要是讲自己在antd的table和一些其它组件的结合用法,用法也比较常见,自己觉得还算一个不错的tutorial,纯属抛砖引玉,大家看了之后,也可以见仁见智~

需求

呵呵嗒,还是ue图起跳:

image

image

image

一个带分页,页码,多选批量操作,行内编辑的一个列表。我们准备用antd的table来实现。

实现

首先来看看我设计的react-redux的store:

`
const initialConditions = {
    pageNo: 1,
    // 行内编辑相关
    isEditing: '', // LEVEL, AUTH_TYPE
    currentRowIndex: -1, // 鼠标hover着的rowIndex
    selectedRows: [] // 勾选上的rows,注意并非rowKey
};

const initialState = {
    ...initialConditions,
    loading: false, // true就要加一个loading的Spin
    list: [], // 数据
    total: 0,
    pageSize: 10
};
`

注意到还拆了一层initialConditions出来,是考虑到刷新的时候(比如delete了列表里面某些行,点击了分页等),需要重新请求新鲜的数据,此时只需要udpate某些状态到initialConditions,再配上当前实际的pageNo即可得到预期的请求参数。

来看看sagas:

`
function* fetchListSaga() {
    yield put(update({loading: true}));
    const result = yield call(authApi.search, {
        pageNo: yield select(makeSelector('pageNo')),
        pageSize: yield select(makeSelector('pageSize'))
    });
    yield put(update({
        loading: false,
        list: result.result,
        total: result.totalCount
    }));
}

const [tableChange, waitTableChange] = createSaga(
    'tableChange',
    function* ({payload: {selectedRowKeys}}) {
        const param = {
            ...initialConditions,
            pageNo: selectedRowKeys.current,
            pageSize: selectedRowKeys.pageSize
        };
        yield put(update(param));
        yield call(fetchListSaga);
    }
);

const [currentRowIndexChange, waitCurrentRowIndexChange] = createSaga(
    'currentRowIndexChange',
    function* ({payload: currentRowIndex}) {
        const isEditing = yield select(makeSelector('isEditing'));
        const modalVisible = yield select(makeSelector('inlineModalVisible'));
        if (!isEditing && !modalVisible) { // 非编辑态时,才update
            yield put(update({currentRowIndex}));
        }
    }
);
`

currentRowIndexChange里面需要注意,如果当前已经在行内编辑,就别更新了,如果此时不慎去更新了currentRowIndex,那Overlay明明还悬浮着,别的行的行内编辑入口却偷偷摸摸出来了。

editingChange(更新isEditing)和selectedRowsChange(更新selectedRows)不说了,就是纯update store而已,没有别的逻辑。

好了,我们来看看UI里面怎么用的table:

`
class AuthList extends PureComponent {

    getRowKey = item => item.userId;

    onVisibleChange = key => visible => {
        visible && this.props.editingChange({isEditing: key});
    }

    onRowChange = (record, index) => {
        const currentRowIndexChange = this.props.currentRowIndexChange;
        return {
            onMouseEnter: () => currentRowIndexChange(index),
            onMouseLeave: () => currentRowIndexChange(-1)
        };
    }
}
`

2个方法也很好理解,一个是将来Dropdown组件的onVisibleChange的回调方法(表示我现在正在行内编辑第hoverIndex行的isEditing这个key的cell),由于Dropdown可以指定overlay,这个overlay我们就可以写成一个业务组件了,传值也有上面model的list[currentRowIndex],完全不成问题。而当你要关闭overlay时,isEditing置为''就可以了。

另外一个方法当然就是table里面row变化的回调,不用多解释,代码很清晰了。

来看看render方法里面antd table的使用:

`
<Table
    loading={loading}
    columns={columns}
    pagination={pagination}
    dataSource={list}
    rowKey={this.getRowKey}
    onChange={tableChange}
    onRow={this.onRowChange}
    rowSelection={{
        selectedRowKeys: selectedRows.map(({userId}) => userId),
        onChange(selectedRowKeys, selectedRows) {
             selectedRowsChange(selectedRows);
        }
    }}
/>
`

pagination不说了哈,主要讲讲columns里面行内编辑怎么配的,这里前面抖过的onVisibleChange包袱终于登场了~

`
const entryProps = {isEditing, currentRowIndex, onVisibleChange: this.onVisibleChange};

const genEditEntry = ({rowIndex, currentRowIndex, overlay, isEditing, onVisibleChange, key}) => (
    <Fragment>
        {
            rowIndex === currentRowIndex && <Dropdown
                overlay={overlay}
                trigger={['click']}
                visible={isEditing === key}
                onVisibleChange={onVisibleChange(key)}
                placement="bottomCenter"
            >
                <Icon type="edit" />
            </Dropdown>
        }
    </Fragment>
);

return [
    {
        title: '授权级别',
        width: 120,
        dataIndex: 'level',
        key: 'level',
        render(text, {level}, rowIndex) {
            return (
                <div>
                    LEVEL文案
                    {
                        genEditEntry({
                            ...entryProps,
                            rowIndex,
                            key: 'LEVEL',
                            overlay: <Level />
                        })
                    }
                </div>
            );
        }
    },
    {
        title: '允许编辑',
        width: 120,
        dataIndex: 'authType',
        key: 'authType',
        render(text, {authType}, rowIndex) {
            return (
                <div>
                    authType文案
                    {
                        genEditEntry({
                            ...entryProps,
                            rowIndex,
                            key: 'AUTH_TYPE',
                            overlay: <AuthType />
                        })
                    }
                </div>
            );
        }
    }
];
`

genEditEntry很巧妙:只有当前hover着的row才出该行的行内编辑entry。然后点击entry时激发的Dropdown的onVisibleChange传key,让key对应的这个column的Dropdown的state变可见。具体你可以看看Dropdown的api,这是一个结合使用的范例~

至于具体的各个Overlay本身,就是另外的业务逻辑了(相当于微型表单),这里不做赘述了。

简单吗?其实很简单,你甚至可以把这些代码封装成套路(list-ria),后续复用起来更加无脑流水线化。另外这个套路依然可以扩展为更加复杂的形态,毕竟表头还是很简单的,antdtable的表头还可以支持筛选,排序等工作,这里ue需求没有提到,都是Jefferson后面待做的功课~

其它

啥都不多说了,antd还是非常强大的,继续学习api吧~

antd Table

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant