本文介绍了Redux中的性能和mergeProps的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的redux容器中,我必须从商店中调出很多复杂的动作。在没有破坏性能的情况下,我找不到合适的模式来解决问题。



让我们以一个只包含发送按钮的容器为例发送消息: / p>

(对于这样一个小例子,以下任何一种方法都可以正常工作,我只是想说明我遇到的更大容器的问题。)



天真的方法 - 将所有参数传递给组件



  function mapStateToProps(state){
return {
user:selectors.selectedUser(state),
title:selectors.title(state),
message:selectors.message(state ),
};
}

函数dispatchProps(state){
return {
onClickSend:function(user,title,message){
actions.sendMessage({user ,标题,消息});
}
};
}

如果我这样做,我的简单发送按钮必须知道许多无用的属性:

  SendBtn.propTypes = {
user:PropTypes.string ,
title:PropTypes.string,
message:PropTypes.string,
onClickSend:PropTypes.func,
}

还要用所有这些道具调用onClickSend:

  onClickSend(用户,标题,消息)

这对于发送按钮。按钮应该只知道它必须调用 onClickSend 当我们点击它时,这个组件是一个简单的按钮。它不应该知道任何其他道具。此外,在一个更复杂的情况下,它可能不仅仅需要2个道具(标题和消息),而是5或10个。



另一个问题是表演,每次我我要修改消息或标题(每次击键时===),发送按钮将被重新渲染。



我当前的方法 - 使用mergeProps将参数传递给操作



我的应用程序当前使用的当前方法是依赖mergeProps:

  function mapStateToProps(state){
return {
user:selectors.selectedUser(state),
title:selectors.title (州),
消息:selectors.message(州),
};
}

函数mergeProps(stateProps,dispatchProps,ownProps){

const {user,title,message} = stateProps;

const newProps = {
onClickSend:actions.sendMessage.bind(null,{
user,
title,
message
}) ;
};

返回R.mergeAll([stateProps,dispatchProps,ownProps,newProps]);
}

我发现这种方法更好,因为发送按钮只需要知道它必须激活其唯一属性:单击时onClickSend。无需担心用户,标题或消息。



但是,这种方法有一个很大的缺点:onClickSend的引用会在每次商店更改时发生变化,在更大的现实案例中导致表现非常糟糕。



第三种方法 - 从redux-thunk访问状态



性能问题的解决方案可能是使用redux-thunk并直接在动作中访问状态。

  //动作文件
var sendMessageAction = createAction(SEND_MESSAGE,function(dispatch,getState){
//由redux thunk执行
const state = getState();

const args = {
user:selectors.selectedUser(state),
title:selectors.title(state),
message:selectors.message(state) ,
}
dispatch(sendMessage(args))
})

//容器文件
函数mapDispatchToProps(dispatch){
onClickSend :dis patch(sendMessageAction())
}

但我不喜欢这种方法,因为:




  • 操作会访问商店 - 它是否会破坏单向流?

  • 操作会注意选择器 - 关注点分离不好?

  • 操作会变成复杂的对象



你的方法



我现在已经在一个大型的redux应用程序上工作了一段时间,这是迄今为止我对Redux的最大痛苦。令人惊讶的是,我没有找到太多关于如何解决它的信息所以我想知道我是否遗漏了一些基本的东西。



你对这个问题的处理方法是什么?

解决方案

你错过了第四种方法(类似于你的 mergeProps option):让父组件传递一个捕获这些值的绑定函数或回调闭包,例如:

  //使用bind()
< SendButton onClick = {sendMessage.bind(null,user,title,message)}

//或者,作为内联箭头函数
SendButton onClick = {()=> this.sendMessage(用户,标题,消息)}

我认为理论上第五种方法可能是连接 SendButton ,让它将一个非常简单的点击处理程序传递给按钮,然后让担心来自其自身道具的参数(即,保持按钮本身完全呈现,并将逻辑放在按钮的父级中)。实际上,我猜这几乎是@samanime的建议。



你使用哪种方法取决于你。我个人可能会倾向于最后一个,只是为了每次重新渲染时它都不会重新创建一个函数。 (特别是,我会避免使用 mergeProps 方法,因为每次商店更新时都会重新创建一个函数,这比组件重新渲染更频繁。)



我实际上在你的博客文章中解决了关于访问状态是否打破单向数据流的问题)。


In my redux containers, I have to dispatch pretty complex actions taking a lot of properties from the store. I cannot find the right pattern to tackle the problem without crushing the performances.

Let's take the example of a container that would only contain a Send button to send a message:

(For such a small example, any of the following approach would work well, I am just trying to illustrate a problem I encounter in way bigger containers.)

Naive approach - Pass all arguments to the component

function mapStateToProps(state) {
  return {
    user:           selectors.selectedUser(state),
    title:          selectors.title(state),
    message:        selectors.message(state),
  };
}

function dispatchProps(state) {
  return {
    onClickSend: function(user, title, message) {
      actions.sendMessage({user, title, message});
    }
  };
}

If I do that, my simple Send button will have to be aware of a lot of useless properties:

SendBtn.propTypes = {
  user:               PropTypes.string,
  title:              PropTypes.string,
  message:            PropTypes.string,
  onClickSend:        PropTypes.func,
}

And also to call onClickSend with all those props:

  onClickSend(user, title, message)

That's way too much knowledge for the Send button. The button should only know it has to call onClickSend when we click on it, this component is a simple button. It shouldn't know about any other props. Also, in a more complex case, it could be not just 2 props (title and message) that are needed but 5 or 10.

Another problem is performances, everytime I am going to modify the message or the title (=== on every keystroke), the send button is going to be re-rendered.

My current approach - Use mergeProps to pass arguments to the actions

The current approach of my app is currently using is relying on mergeProps:

function mapStateToProps(state) {
  return {
    user:           selectors.selectedUser(state),
    title:          selectors.title(state),
    message:        selectors.message(state),
  };
}

function mergeProps(stateProps, dispatchProps, ownProps) {

  const {user, title, message} = stateProps;

  const newProps =  {
    onClickSend: actions.sendMessage.bind(null, {
      user,
      title,
      message
    });
  };

  return R.mergeAll([stateProps, dispatchProps, ownProps, newProps]);
}

I find this approach way better because the Send button only has to know that it must fire its unique property: onClickSend when clicked. No need to worry about user, title or message.

However, this approach has a huge drawback: the reference of onClickSend is going to change everytime the store changes, which would lead to really poor performances on larger real-life cases.

A third approach - Access state from redux-thunk

A solution to the performance issue could be to use redux-thunk and access the state directly in the action.

// Action file
var sendMessageAction = createAction("SEND_MESSAGE", function(dispatch, getState) {
  // executed by redux thunk
  const state = getState();

  const args = {
    user:    selectors.selectedUser(state),
    title:   selectors.title(state),
    message: selectors.message(state),
  }
  dispatch(sendMessage(args))
})

// Container file
function mapDispatchToProps(dispatch) {
  onClickSend: dispatch(sendMessageAction())
}

But I don't like this approach because:

  • actions would access the store - doesn't it break the unidirectional flow?
  • actions would be aware of the selectors - bad separation of concern?
  • actions would become complex objects

Your approach

I have worked for a while on a big redux app now and it is by far the biggest pain I have with Redux. Surprisingly, I don't find too much information about how to solve it so I'm wondering if I'm missing something elementary.

What is your approach of that problem?

解决方案

You're missing a fourth approach (similar to your mergeProps option): have the parent component pass a bound-up function or callback closure that captures those values, like:

// using bind()
<SendButton onClick={sendMessage.bind(null, user, title, message)}

// or, as an inline arrow function
SendButton onClick={() => this.sendMessage(user, title, message)}

I suppose in theory a fifth approach might be to connect the parent of the SendButton, have it pass a very simple click handler to the button, and let the parent worry about the arguments from its own props (ie, keep the button itself completely presentational, and put the logic in the parent of the button). Actually, I guess that's pretty much the suggestion that @samanime had.

Which approach you use is really up to you. I personally would probably lean towards that last one, just so that it doesn't re-create a function every time there's a re-render. (In particular, I would avoid the mergeProps approach, as that will re-create a function every time the store updates, which will be even more often than the component re-renders.)

I actually addressed your questions about whether accessing state in thunks breaks "uni-directional data flow" in my blog post Idiomatic Redux: Thoughts on Thunks, Sagas, Abstraction, and Reusability. As a summary, I don't think it actually does break that unidirectional flow, and my opinion is that accessing state in thunks is a completely valid approach.

As a side note, I generally recommend that people use the object shorthand argument for binding methods with connect, instead of writing an actual mapDispatchToProps function:

const mapDispatch = {
    onClickSend : actions.sendMessage
};

export default connect(mapState, mapDispatch)(MyComponent);

To me, there's almost never a reason to actually write out a separate mapDispatch function (which I also talk about in my post Idiomatic Redux: Why Use Action Creators?).

这篇关于Redux中的性能和mergeProps的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-14 08:03