使用redux创建切换“全部”,“在线”和“离线”操作的最佳方法是什么?

时间:2017-05-06 10:57:57

标签: reactjs redux reducers

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

我正试图找出最好的方法来做到这一点。目前,我的“用户”状态只是一个用户数组,每个用户都是一个包含2个键的对象:channelDatastreamData

这是因为我必须对twitch API进行2次axios调用:一次是获取一般频道信息(例如徽标网址,频道名称),另一种是查看频道当前是在线还是离线。< / p>

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);

1 个答案:

答案 0 :(得分:0)

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

考虑以下问题:

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,远离生产代码,但我希望它能指出你的正确轨道。