[英]Passing more parameters to a pure render function in React
最近我一直试图把我的React组件写成“Pure Functions”,我注意到有时候我想要的东西感觉很像state
。 我正在考虑将我的state
作为第二个参数传递给我的组件。 我可以通过使用两个参数props
和state
调用我的组件作为普通函数来实现这一点。
例如:
// abstracted to it's own module
const useState = (Component, state = {}) => {
return class extends React.Component {
state = createState(this, state); // will traverse and update the state
render() {
const { props, state } = this;
return Component(props, state); // <-- call the Component directly
}
};
};
const Component = (props, { index, increase }) => (
<div onClick={increase} {...props}>
Click me to increase: {index}
</div>
);
const componentState = {
index: 0,
increase: (event, state) => ({ ...state, index: state.index + 1 })
};
const StatefullComponent = useState(Component, componentState);
<StatefullComponent style={{ color: "purple" }} />;
我有一个CodeSandbox示例:
我的问题是:
shouldComponentUpdate
这样的shouldComponentUpdate
吗? (我有一种下沉的感觉,这是建模旧的context
api) 注意 :我在此示例中使用state
,但它也可能是theme
,授权规则或您可能希望传递到组件中的其他内容。
乍一看,当我检查你的代码时,我有一个问题:
“你为什么要这么复杂?当你可以简单地用类宣言制作它”。
但是后来当我分割你的代码时,我发现真的值得这样做。
问题1:没有真正有所作为,这是HOC如何构成的方式。
我不再用状态值扩展道具,这可能是件好事
为什么/何时可能是一件好事?
我正在搞乱组件默认呈现的方式,这可能是件坏事
我没有看到你默认破坏或混乱渲染,我认为HOC模式促进了相同的哲学,你将状态与道具分开的区别。
问题2:如果开发人员决定使用无状态组件,那么他/她应该意识到所有“生命周期方法”或参考ref
将不可用。
你的模式使无状态组件成为“有状态”但在无状态声明中 - 令人惊讶的😋。
就像在JSX
一样,你在JS中用“HTML”编写,在其中用另一个“HTML”编写JS代码:
<ul>
{list.map(text => <li>text</li>)} // I know there should be used key
</ul>
Mr. Baudin
模式(国家充满无国籍):
import React from 'react'
import {useState} from './lib'
const state = {
index: 0,
increase: (event, state) => ({index: state.index + 1})
}
const Component = (props, state) => (
<div onClick={state.increase} {...props}>
Click me to increase: {state.index}
</div>
)
export default useState(Component, state)
问题3:这取决于未来版本中的更改。
问题4:嗯......我不认为提供的模式(已实现的库)可以被视为应用程序状态管理,但它可以在任何状态管理中使用,如Redux或Mobx,因为它处理内部组件状态。
问题5:不,我不认为。 您的解决方案使代码更简洁。 功能组件适用于非常简单或具有代表性的组件,现在可以通过状态进行扩展。
虽然这个问题已经公开,但我已经对这个问题做了一些痛苦的研究,我想和你分享这个研究。
问题1:表现; 将组件作为函数或甚至作为构造函数调用并不会产生任何影响。 您只需获取组件而不是类型。
// the component
const MyComponent = () => (<div>This is my page</div>);
console.log(MyComponent());
console.log(new MyComponent());
console.log(<MyComponent />);
console.log(React.createElement(MyComponent));
笔 (别忘了检查开发人员工具!)
我注意到的是,当你直接调用组件时会丢失一些信息,例如,当我使用JSX时,类型信息会被保留:
React.createElement(MyComponent).type === MyComponent // <- true
MyComponent() // <- Now way to find out what constructed this...
这似乎不是什么大问题,因为MyComponent()
被视为普通div
因此它应该正确呈现; 但我可以想象React可能会对组件的类型进行一些查找,并调用可能会影响性能的函数。
在文档和源代码中都没有找到任何暗示这种情况的内容,因此我认为没有理由担心此时的性能。
问题2:这是否打破了shouldComponentUpdate
; 答案是“也许不是”,但不是因为我需要像建议的那样写一个class
。 问题是,当您使用PureComponent
时,React会对props
进行浅层比较,而纯函数只需要使用相同的道具获得相同的结果。 在我的情况下,由于第二个参数,它可能认为组件不需要更新,但实际上它应该。 由于我的实现中有一些魔力,这似乎适用于用useState
函数包装的根组件的子组件。
这正如我所期望的那样,与context
api的原始实现相同。 因此,我应该能够使用一些反应技术来解决它。
问题3:看到“只是将一个组件作为一个函数调用”似乎是反应背后的整个想法,并且看到它如何在没有原始类型信息的情况下产生几乎完全相同的组件,我认为没有理由在未来将其中断。
问题4/5:不,没有更多的“Reacty”方法可以真正解决这个问题。 有一种更实用的方式。 我可以使用州monad并举起整个东西; 但这会涉及很多工作,我真的看不到这样做的好处。 将状态作为第二个参数传递似乎至少在目前看来可能是奇怪但可行但实际上可行的东西。
问题5:当我开始环顾四周时,我没有找到很多这些问题的答案,但是现在我已经真正挖掘了自己,我可以看到其他几个库做同样的事情。 例如: 重新组合自称为“lodash for react”。 他们似乎使用这种模式将你的组件包装在一个函数中并且返回一个类很多。 (他们的withState实现)。
额外信息:我的结论是这种模式(因为它只不过是一种模式)是有效的,并没有违反React的任何基本规则。 只是为了提供一些额外的信息,贝尔纳多·费雷拉·巴斯托斯·布拉加写道,我需要用一个class
来做“反应方式”。 我没有看到如何包装你的函数并返回一个带有状态的类是“使用类”之外的任何东西。
但我确实认为包装函数会增加复杂性,但不会增加太多; 函数调用是真正优化的,因为您编写的是可维护性并在以后进行优化。
我最担心的一个问题是,当软件变得越来越复杂并且我们需要处理越来越多的交叉问题时,作为一个参数处理每个问题将变得越来越困难。 在这种情况下,最好使用解构模式从“关注”对象中获取所需的关注点作为第二个参数传递。
关于这种模式的最后一件事。 我做了一个小测试(只是selenium渲染一个页面100次),这个模式,小规模,比使用Redux更快。 您的redux状态越大,连接的组件越多,此模式变得越快。 不利的一面是,您现在正在进行一些手动状态管理,这会带来复杂的实际成本。 记住要权衡所有选项。
与用户交互的应用程序要求您尝试跟踪他们的交互。 这些交互可以用不同的方式建模,但我真的很喜欢有状态的方法。 这意味着您通过应用程序“线程”状态。 现在,您可以通过几种方式创建组件。 我想提到的三个是:
Component
扩展 PureComponent
扩展 我真的很喜欢最后一个选项但是,说实话,保持代码性能很难。 我们在那里有很多文章解释了每次调用组件时lambda表达式如何创建一个新函数,打破了PureComponent
完成的props
的浅层比较。
为了抵消这种情况,我使用了一种模式,我将无状态组件包装在HoC中 ,我传递组件和状态对象。 这个HoC做了一些魔术并将状态作为第二个参数传递给无状态函数,确保当通过PureComponent
的比较测试道具时它应该起作用。
现在为了使包装器更好,我会记住lambdas,以便只存在对该函数的单个引用,这样即使你通过引用测试函数,它仍然可以。
我用的代码是:
return Object.entries(_state).reduce(
(acc, entry) => {
const [key, value] = entry;
if (value instanceof Function) {
acc[key] = _.memoize(item => (...args) => {
const newState = value.apply(null, [...args, root.state, root.props]);
root.setState(newState);
});
} else {
acc[key] = value;
}
return acc;
},
{}
);
};
正如你所看到的,我记住了这个函数并将其称为代理参数并传入状态和道具。 只要您可以使用如下所示的唯一对象调用这些函数,此方法就可以正常工作:
const MyComponent = useState((props, { items, setTitle }) => {
return (
<div>
{items.map(item => (
<Component key={item.id} item={item} changeItem={setTitle(item)} />
))}
</div>
);
}, state);
1-这种模式会损害性能吗?
性能通常不是黑/白,而是在不同情况下更好/更差。 由于React已经有了这样做的标准方法,因此你可能会错过内部优化。
2 - 这个模式会破坏像shouldComponentUpdate这样的东西吗? (我有一种下沉的感觉,这是建模旧的上下文api)
是的,如果需要编写shouldComponentUpdate函数,则应该使用类声明
3-我有多担心未来的反应更新是否会破坏此代码?
我认为你应该这样做是公平的,因为有明显的,有记录的方法可以使用类来做同样的事情。
4 - 在不使用像Redux这样的库的情况下,是否有更多的“Reacty”方法在Pure函数中使用State?
您可以拥有一个具有状态的容器组件,并传递回调函数来更新状态
5-我试图解决一些不应该解决的问题?
是的 ,因为已经有一种主流和记录的方法来使用Component类来满足您的需求。 您应该只为非常简单或表示的组件使用功能组件
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.