在React-Redux中实时重新排序列表的最佳方法?

时间:2018-12-13 08:10:09

标签: javascript sorting react-redux

我当前正在构建一个具有实时列表的应用程序。列表视图如下图: enter image description here

当以下操作之一被触发时,此列表将实时更新:

  • 消息已更新,updated_time将更改
  • 接收新消息
  • 接收邮件的未读状态

所以我想基于两个条件对该列表进行排序:

  1. 所有unRead_message始终位于顶部
  2. 所有邮件均应按updated_time DESC(最新的优先顺序)进行排序,除非始终处于优劣状态的unread_message除外。

此列表的数据受以下reduceres的控制:

    export const data = createReducer(
    {},
    {
        [ CONVERSATIONS_RECEIVE ]: ( state, { conversations } ) => {
            return reduce(
                conversations,
                ( memo, conversation ) => {
                    const { id } = conversation.data;

                    if ( memo === state ) {
                        memo = { ...memo };
                    }

                    memo[ id ] = conversation;
                    return memo;
                },
                state
            );
        },
        [ RECEIVED_PENDING_MESSAGE ]: ( state, { data } ) => {

            if ( !data || !data.conversation_id in state ) return state;

            return update( state, {
                [ data.conversation_id ]: { data: { $merge: { snippet: data.message, updated_time: data.created_time } } }
            } )
        },
        [ RECEIVED_CONVERSATION_SEEN ]: ( state, { data } ) => {
            if ( !data || !data.conversation_id in state ) return state;

            return update( state, {
                [ data.conversation_id ]: { data: { $merge: { seen: true } } }
            } )
        },
        [ RECEIVED_CONVERSATION_UNSEEN ]: ( state, { data } ) => {
            if ( !data || !data.conversation || !data.conversation.id in state ) return state;

            return update( state, {
                [ data.conversation.id ]: { data: { $merge: { seen: false } } }
            } )
        },
    }
);

export function keys( state = [], action ) {
    switch ( action.type ) {
        case CONVERSATIONS_RECEIVE:
            if ( !action.conversations || !action.conversations.length ) return state;
            return state.concat( action.conversations.map( conversation => conversation.data.id ) );
        default:
    }

    return state;
}

export default combineReducers( {
    keys,
    data,
    isRequesting,
} );

由键数组呈现的列表数据:

[
  "id_1",
  "id_2",
  //....
]

和数据:

[
    {
        "data": {
            "can_comment": false,
            "can_hide": false,
            "can_like": false,
            "can_reply": true,
            "can_reply_privately": false,
            "comment_id": "",
            "id": "hbiocjgwxpncbnja8a3rra4oke",
            "is_hidden": false,
            "is_private": false,
            "last_seen_by": "ckj7mny56jrmir4df8h466uk7a",
            "message": "",
            "page_id": "1651651651651651",
            "post_id": "",
            "private_reply_conversation": "null",
            "scoped_thread_key": "t_1221211454699392",
            "seen": true,
            "snippet": " [ TRI ÂN VÀNG-NGÀN ƯU ĐÃI ] \nMiễn phí ship tận nhà \nChỉ còn 350k trọn bộ COMBO 3 sản phẩm tuyệt vời cho chị em và các bé nhà mình : \n Vòng tay chỉ đỏ kim vàng đính đá topaz được nhập từ Thái Lan \n Dây chuyền hồ ly hợp mệnh \n Vòng tay dâu tằm cho bé \nKhuyến mại chỉ áp dụng với những khách hàng đáng yêu nhận được tin nhắn này.Nhanh tay để lại SỐ ĐIỆN THOẠI để được thỉnh bộ sản phẩm với giá siêu khuyến mại này Chị  Chị Hai nha ",
            "type": "message",
            "unread_count": 0,
            "updated_time": "2018-12-07T12:00:21+0000"
        },
        "tags": [],
        "from": {
            "id": "1223645637789307",
            "name": "Chị Hai"
        }
    },
    {
        "data": {
            "can_comment": false,
            "can_hide": false,
            "can_like": false,
            "can_reply": true,
            "can_reply_privately": false,
            "comment_id": "",
            "id": "7oemjmkpxidi7cgk99ggbdamdw",
            "is_hidden": false,
            "is_private": false,
            "last_seen_by": "ckj7mny56jrmir4df8h466uk7a",
            "message": "",
            "page_id": "1651651651651651",
            "post_id": "",
            "private_reply_conversation": "null",
            "scoped_thread_key": "t_279998559200944",
            "seen": true,
            "snippet": "Giá hơi cao",
            "type": "message",
            "unread_count": 0,
            "updated_time": "2018-12-07T12:00:19+0000"
        },
        "tags": [],
        "from": {
            "id": "280004999200300",
            "name": "Chung Ngoc"
        }
    },
    // ..... other items
]

我尝试过:

function mysortfunction(a, b) {

    // always sort by updated_time first
    if ( a.data.updated_time > b.data.updated_time ) return 1;
    if ( a.data.updated_time <= b.data.updated_time ) return -1;

    // if BOTH a.seen == false AND b.seen == false, we'll sort a & b by updated_time
    if ( !a.data.seen && !b.data.seen ) {
        if ( a.data.updated_time > b.data.updated_time ) return 1;
        if ( a.data.updated_time <= b.data.updated_time ) return -1;
    }

    return 0;
}

任何人都可以告诉我根据上述条件对列表进行排序的最佳方法,非常感谢!

2 个答案:

答案 0 :(得分:1)

我没有清楚地阅读所有代码,但是您只能使用javascript函数对列表进行重新排序,您将使用sort()方法按updated_time对所有列表进行排序,然后查看是否可见,您必须放置一个函数排序方法,但唯一的问题是,传递来进行比较的函数需要一个int值作为结果,请参见其this link。 这样就可以做到这一点,在进行比较时,如果他的消息不是seaan,请添加到UPDATED_TIME +1000000000,例如,所有看不见的味精将排在首位,并且所有消息都使用一个函数进行排序

答案 1 :(得分:1)

您可以通过以下方式对多个值进行排序:

const data = [
  { seen: true, updated_time: 'A' },
  { seen: true, updated_time: 'A' },
  { seen: false, updated_time: 'A' },
  { seen: false, updated_time: 'A' },
  { seen: false, updated_time: 'B' },
];
//compare booleans, returns -1,0 or 1
const compareSeen = (direction) => (a, b) =>
  a.seen === b.seen
    ? 0
    : a.seen
      ? -1 * direction
      : 1 * direction;
//compare strings, returns -1, 0 or 1
const compareDate = (direction) => (a, b) =>
  a.updated_time.localeCompare(b);

//pass array of compare functions and return a function that takes
//  a and b and keeps using compare functions until one of them returns non zero
const createSort = (comparers = []) => (a, b) =>
  comparers.reduce(
    (result, compareFn) =>
      result === 0 ? compareFn(a, b) : result,
    0,
  );
console.log(
  data
    .slice() //sort will mutate, we don't want that so we make a copy first
    .sort(createSort([compareSeen(-1), compareDate(1)])),
);

如果您具有来自不同timeZone的ISO时间,则不能使用字符串比较,而必须先将日期字符串映射到数字,然后再映射回数字:

const data = [
  { seen: true, updated_time: '2018-12-07T12:00:21+0000' },
  { seen: true, updated_time: '2018-12-07T12:00:21+0000' },
  { seen: false, updated_time: '2018-12-07T12:00:21+0000' },
  { seen: false, updated_time: '2018-12-07T12:00:21+0000' },
  { seen: false, updated_time: '2018-12-07T12:00:21+0100' },
];
//compare booleans, returns -1,0 or 1
const compareSeen = (direction) => (a, b) =>
  a.seen === b.seen
    ? 0
    : a.seen
      ? -1 * direction
      : 1 * direction;
//compare numbers, returns negative number, 0 or positive number
const compareDate = (direction) => (a, b) =>
  (a.updated_time - b.updated_time) * direction;

//pass array of compare functions and return a function that takes
//  a and b and keeps using compare functions until one of them returns non zero
const createSort = (comparers = []) => (a, b) =>
  comparers.reduce(
    (result, compareFn) =>
      result === 0 ? compareFn(a, b) : result,
    0,
  );
console.log(
  data
    .map((item) => ({
      ...item,
      updated_time: new Date(item.updated_time).getTime(),
    })) //map already copied data so sort will not mutate it
    .sort(createSort([compareSeen(-1), compareDate(-1)]))
    .map((item) => ({
      ...item,
      updated_time: new Date(
        item.updated_time,
      ).toISOString(),//note: now you have strings in UTC/Zulu time
    })),
);