使用 redux 创建切换 'All'、'Online' 和 'Offline' 操作的最佳方法?

Best way to create toggle 'All', 'Online', and 'Offline' actions with redux?

我正在使用 React 和 Redux 创建 freecodecamp twitch API。该应用程序的想法是您可以搜索要关注的频道,这些频道将添加到 'Friends List'。在好友列表的顶部是一个按钮组,具有 3 个切换功能:显示全部、显示在线和显示离线。

我正在尝试找到执行此操作的最佳方法。目前我的 'users' 状态只是一个用户数组,每个用户都是一个具有 2 个键的对象:channelDatastreamData

这是因为我必须对 twitch API 进行 2 次 axios 调用:一次获取一般频道信息(例如徽标 url、频道名称),另一次查找如果频道当前在线或离线。

actions/index.js:

import axios from 'axios';

export const FETCH_USER = 'FETCH_USER';
export const HAS_ERRORED = 'HAS_ERRORED';
export const SELECTED_USER = 'SELECTED_USER';
export const SHOW_ONLINE = 'SHOW_ONLINE';
export const SHOW_OFFLINE = 'SHOW_OFFLINE';
export const SHOW_ALL = 'SHOW_ALL';

const ROOT_URL = 'https://wind-bow.glitch.me/twitch-api/';

export function fetchUser(user) {
  const channelUrl = `${ROOT_URL}/channels/${user}`;
  const streamUrl = `${ROOT_URL}/streams/${user}`;

  const request = axios.all([
    axios.get(channelUrl),
    axios.get(streamUrl)
  ]);

  return (dispatch) => {
    request.then(axios.spread((channelData, streamData) => {
      var channelData = channelData.data;
      var streamData = streamData.data;

      if (channelData.error) {
        dispatch(hasErrored(channelData.message));
      }
      else {
        dispatch({
          type: FETCH_USER,
          payload: { channelData, streamData }
        });
        dispatch(hasErrored(null));
      }
    }));
  }
}

export function hasErrored(msg) {
  return {
    type: HAS_ERRORED,
    msg
  }
}

export function selectUser(user) {
  return (dispatch) => {
    dispatch(hasErrored(null));
    dispatch({
      type: SELECTED_USER,
      user
    });
  }
}

export function showOnline() {
  return {
    type: SHOW_ONLINE
  }
}

export function showOffline() {
  return {
    type: SHOW_OFFLINE
  }
}

export function showAll() {
  return {
    type: SHOW_ALL
  }
}

然后根据单击哪个按钮,将执行 SHOW_ALL、SHOW_ONLINE 或 SHOW_OFFLINE 操作,目前只是过滤 当前状态,以查看该用户的 streamData 是否显示它在线。但是这种方法的问题是当我点击一个按钮时,例如SHOW_ONLINE,它会列出所有在线用户,不会将所有离线用户存储在某个地方,所以当我点击 SHOW_OFFLINE 之后,

reducers/reducer_users.js:

import { FETCH_USER, SHOW_ONLINE, SHOW_OFFLINE, SHOW_ALL } from '../actions/index';

export default function(state = [], action) {

  switch(action.type) {


    case FETCH_USER:
    return [action.payload, ...state];

    case SHOW_ONLINE:
    return state.filter(user => {
      return user.streamData.stream;
    });

    case SHOW_OFFLINE:
    return state.filter(user => {
      return !user.streamData.stream;
    });

    case SHOW_ALL:
    return state;

  }
  return state;
}

containers/users_list.js:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { selectUser, showOnline, showOffline, showAll } from '../actions/index';

class UsersList extends Component {

  renderUser(user) {
    const { channelData, streamData } = user;

    return (
      <tr
        key={channelData.display_name}
        onClick={() => this.props.selectUser(user)}
        className='list-item'>
        <td>
          <img src={channelData.logo} className='user-logo' />
        </td>
        <td>
          {channelData.display_name}
        </td>
        <td>
          {streamData.stream ?
            <span className='online'>Online</span> :
            <span className='offline'>Offline</span>}
        </td>
      </tr>
    )
  }

  render() {
    console.log('state:', this.props.users);
    return (
      <div className='col-sm-4'>
        <div className='text-center'>
          <div className='btn-group btn-group-sm' role='group'>
            <button
              className='btn btn-default'
              onClick={this.props.showAll}>
              All
            </button>
            <button
              className='btn btn-default'
              onClick={this.props.showOnline}>
              Online
            </button>
            <button
              className='btn btn-default'
              onClick={this.props.showOffline}>
              Offline
            </button>
          </div>
        </div>
        <table className='table table-hover'>
          <thead>
            <tr>
              <th>Logo</th>
              <th>Channel</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            {this.props.users.map(this.renderUser.bind(this))}
          </tbody>
        </table>
      </div>
    )
  }
}

function mapStateToProps({ users }) {
  return { users };
}

export default connect(mapStateToProps, { selectUser, showOnline, showOffline, showAll })(UsersList);

如果我理解正确的话,你的问题是排序、过滤等问题,同时保持对原始状态的引用。

考虑以下问题:

let arr = [1,2,3,4];

let newArr = arr.filter((item)=>{ return item > 2 });

newArr(在您的情况下,这是您的新状态)现在缺少物品。如果你想查看原始列表,那是不可能的。

在对数据进行任何修改之前,您需要保留一份干净的数据副本,这样无论何时过滤,您总是可以重新开始。

一个更简单的减速器看起来像这样

function(state = {}, action) { //notice state is an object

  switch(action.type) {

    case FETCH_USER:
        let newState = Object.assign({},state);
        newState['pristine'] = Object.assign({},action.payload); 
        newState['filteredData'] = Object.assign({},action.payload);
    return newState;

    case SHOW_ONLINE:
       let data = Object.assign({},state.pristine);
       let filteredData = data.filter(user => {
         return user.streamData.stream;
       });

       return Object.assign({},state,{filteredData});

    case SHOW_ALL:
    return state.pristine;

  }
  return state;
}

此代码有很多防御性 Object.assign 并且远非生产代码,但我希望它能为您指明正确的轨道。