react redux中如何防止重复的列表对象

Posted

技术标签:

【中文标题】react redux中如何防止重复的列表对象【英文标题】:How prevent duplicated list object in react redux 【发布时间】:2019-07-05 03:47:36 【问题描述】:

我的组件存在重复问题,我从 api 获取了一些虚拟数据。

以下哪种方法一切正常,但是当我更改路线并转到另一个页面并返回到列出我的用户对象的页面时。每次都有我的对象的重复列表。我怎样才能防止它?

这是我的组件:

import React,  Component  from 'react';
import  connect  from 'react-redux';
import  bindActionCreators  from 'redux' 
import getAllUsers from '../actions/user'

class users extends Component 
  componentDidMount() 
    this.props.getAllUsers()
  

  render() 
    let usersList = this.props.users.map(user => <li key=user.id>user.name</li>)

    return (
      <div>
        <h1>user list</h1>
        <ul>
          usersList
        </ul>
      </div>
    )
  


const mapStateToProps = state => (
  users: state.users
)


function mapDispatchToProps(dispatch) 
  return 
    getAllUsers: bindActionCreators(getAllUsers, dispatch)
  
 

export default connect(mapStateToProps, mapDispatchToProps)(users)

这是我的减速机:

import  FETCH_USERS  from '../actions/user'

let initialState = []

export default (state = initialState, action) => 
    switch (action.type) 
        case FETCH_USERS:
            return [...state, ...action.payload]
        default:
            return state
    

这是我的行动:

export const FETCH_USERS = 'FETCH_USERS';

export const getAllUsers = () => 
    return(dispatch) => 
        fetch('https://jsonplaceholder.typicode.com/users')
        .then(res => res.json())
        .then(users => 
            dispatch(
                type: FETCH_USERS,
                payload: users
            )
        )
    

【问题讨论】:

因为每次您的组件挂载它都会调用 fetch 用户方法,而不是用新数据替换您的状态,而是将新数据推送到当前状态。将[...state,...action.payload] 替换为[...action.payload] @Alexis 明白了。我修好了,一切正常。非常感谢 【参考方案1】:

目前有几个选项可供您选择:

    评估是否需要执行 API 请求。如果您已经发出了请求,您可以只依赖 Redux 存储中以前缓存的数据,还是必须依赖每次加载组件时获取新用户?
componentDidMount() 
    if (!this.props.users.length) 
        this.props.getAllUsers();
    

    如果每次挂载组件时都需要获取新数据,为什么不在组件卸载时清除 Redux 存储中的数据?例如

组件:

componentWillUnmount() 
    this.props.clearUsers();

减速机:

import  FETCH_USERS, CLEAR_USERS  from '../actions/user'

let initialState = []

export default (state = initialState, action) => 
    switch (action.type) 
        case FETCH_USERS:
            return [...state, ...action.payload]
        case CLEAR_USERS:
            return initialState
        default:
            return state
    

还有其他一些选择可以调查,但希望能解决您的问题 :)

编辑:

正如 cmets 中所指出的,作者的 reducer 有点到处都是。我希望将减速器重构为以下内容:

import  FETCH_USERS  from '../actions/user'

const initialState = 
    users: [],


export default (state = initialState, action) => 
    switch (action.type) 
        case FETCH_USERS:
            return 
                ...state,
                users: [...action.payload],
            
        default:
            return state
    

这将解决每次安装组件时重复用户的问题,同时确保将来可以扩展减速器,而不会冒整个状态被替换的风险。

【讨论】:

它的方法太好了。谢谢 @user3348410 好方法,但无论如何要避免做 [...state,...action.payload] 因为你得到相同的数据。 @Alexis 好点,但是,当您开始扩展您的操作时,您可能会因为您将覆盖整个状态而绊倒。 @tombraider 有效负载包含用户数组,如果您不这样做,它将不会检测到状态的更新,并且如果数据已更改,也不会触发组件的刷新。并且使用您的第一种方法,即使数据已更新,它也永远不会刷新数据。使用第二种方法,您将执行 2 个操作,而不仅仅是刷新用户。如果您只需要每次都加载它们,那么清除它们是没有用的。 作者的 reducer 需要做一些工作,我会使用对象而不是数组作为初始状态,但我坚持我的观点,如果你不传播你的状态,它就是如果您扩展该减速器,将会引起问题。我会更新我的答案。【参考方案2】:

在这里,每次您的组件为 mounting 时,您都在调用 Reducer 的 fetchUser 方法。 当您的router 更改路由时,组件为unmount 并在返回后重新安装fetchUser 再次调用。

在您的 FETCH_USERS 操作中,您将新数据推送到当前状态,而不是用新数据替换旧数据。

您的问题将通过将[...state,...action.payload] 替换为[...action.payload] 来解决

【讨论】:

【参考方案3】:

只要您所在的州只有用户,Alexis 的建议就可以正常工作。一旦您添加更多属性,这将不再起作用,因为它会清除整个状态。

在您的 FETCH_USERS 案例中尝试这样的操作:

让 newState = ...state;

newState.users = [];

newState.users.push(action.payload);

返回新状态;

【讨论】:

这就是为什么你必须创建一个特定的 store 并按数据类型组合 reducer,这比在你的 state 中管理许多不同的属性要好。 他的减速器可能会随着更多的属性而增长。如果要为此减速器添加更多属性,则清除此减速器的整个状态将中断。 感谢您将我的回答标记为无用,Alexis。【参考方案4】:

问题在于,每次遇到减速器中的 FETCH_USERS 案例时,它都会在现有数据(状态)之上附加额外数据(action.payload)。由于每次安装组件时都会发生这种情况,因此它会频繁地将旧数据附加到数组中。以下是更详细的情况说明:

如果您的有效负载等于数组 ["id1", "id2"],那么组件第一次挂载时,它将进入将要生成的状态

state = ["id1", "id2"]

下次加载组件时,用户数组仍然等于 [ "id1", "id2" ],可能添加了新用户或不再存在一些用户。假设还添加了“id3”。 reducer 接受这个并将其附加到之前的状态:

state = ["id1", "id2"]
action.payload = ["id1", "id2", "id3"]

所以...

return [...state, ...action.payload] = [ "id1", "id2", "id1", "id2", "id3" ]

添加重复。

要解决此问题,您只需使用有效负载填充状态,而不是保留以前的状态,因为正在再次检索用户列表而不过滤掉以前存在的用户

return [...action.payload]

你的 reducer 现在应该如下所示:

import  FETCH_USERS  from '../actions/user'

let initialState = []

export default (state = initialState, action) => 
    switch (action.type) 
        case FETCH_USERS:
            return [...action.payload]
        default:
            return state
    

【讨论】:

以上是关于react redux中如何防止重复的列表对象的主要内容,如果未能解决你的问题,请参考以下文章

当在 ReactJs React-Redux 中仅创建或更新列表中的一个项目时,如何停止重新渲染整个项目列表?

React Redux - 删除对象的数据获取模式

如何避免 redux 中的重复代码(鸭子方法)?

在列表中使用 react redux 钩子会影响性能吗?

如何从状态填充Redux-Form选择/下拉列表

在 Redux React 中传递对象以更改 [重复]