使用 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 个键的对象:channelData
和 streamData
。
这是因为我必须对 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 并且远非生产代码,但我希望它能为您指明正确的轨道。
我正在使用 React 和 Redux 创建 freecodecamp twitch API。该应用程序的想法是您可以搜索要关注的频道,这些频道将添加到 'Friends List'。在好友列表的顶部是一个按钮组,具有 3 个切换功能:显示全部、显示在线和显示离线。
我正在尝试找到执行此操作的最佳方法。目前我的 'users' 状态只是一个用户数组,每个用户都是一个具有 2 个键的对象:channelData
和 streamData
。
这是因为我必须对 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 并且远非生产代码,但我希望它能为您指明正确的轨道。