最近我一直试图将我的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
之类的东西吗? (我有一种下沉的感觉,这是对旧的context
api)建模 注意:我在此示例中使用state
,但它也可能是theme
,授权规则或您可能希望传递给您的其他内容成分
<小时/> 编辑19-03-2018 :我注意到人们似乎对我所问的问题感到困惑。我没有寻找新的框架或关于&#34;为什么要分开您的顾虑?&#34;。我很确定这种模式会清理我的代码并使其更易于测试并且更清洁&#34;一般来说。我真的想知道React框架是否会以任何方式阻碍这种模式。
答案 0 :(得分:2)
乍一看,当我检查你的代码时,我有一个问题:
“你为什么要这么复杂?当你可以简单地用类声明来做它”。
但后来当我splitted your code时,我觉得这样做真的很值得。
问题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 :(得分:1)
虽然这个问题已经公开,但我已经对这个问题做了一些痛苦的研究,我想和你分享这个研究。
问题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));
Pen(不要忘记检查开发人员工具!)
我注意到的是,当你直接调用组件时会丢失一些信息,例如,当我使用JSX时,类型信息会被保留:
React.createElement(MyComponent).type === MyComponent // <- true
MyComponent() // <- Now way to find out what constructed this...
这似乎不是什么大问题,因为MyComponent()
被视为正常div
所以它应该正确呈现;但我可以想象React可能会对组件的类型进行一些查找,并调用这样可能会影响性能的函数。
Haven在文档和源代码中都没有发现任何情况,因此我认为没有理由担心此时的性能。
问题2:这是否会导致shouldComponentUpdate
;答案是&#34;也许不是&#34;,但不是因为我需要按照建议编写class
。问题是当你使用props
时,React对PureComponent
进行浅层比较,而纯函数只需要使用相同的道具得到相同的结果。在我的情况下,由于第二个参数,它可能认为组件不需要更新,但实际上它应该。由于我的实现中有一些魔力,这似乎适用于用useState
函数包装的根组件的子组件。
这就像我预期的问题一样,与context
api的原始实现相同。因此,我应该能够使用一些反应技术解决它。
问题3:了解&#34;只是将一个组件称为一个功能&#34;似乎是反应背后的整个想法,并看到它如何产生几乎完全相同的组件没有原始类型信息,我认为没有理由为什么这将在未来打破。
问题4/5:不,没有更多&#34; Reacty&#34;真正解决这个问题的方法。有一种更实用的方式。我可以使用州monad并举起整个东西;但这会包含很多工作,我真的无法看到这样做的好处。将状态作为第二个参数传递似乎至少在目前看来可能是奇怪但可行但实际上可行的东西。
问题5:当我开始环顾四周时,我没有找到很多解决这些问题的答案,但是现在我已经真的挖了自己,我可以看到一些其他图书馆做同样的事情。例如:recompose自称&#34; lodash for react&#34;。他们似乎使用这种模式将你的组件包装在一个函数中并且返回一个类很多。 (他们的withState实施)。
额外信息:我的结论是这种模式(因为它只不过是一种模式)是有效的,并没有违反React的任何基本规则。为了给出一些额外的信息,贝尔纳多·费雷拉·巴斯托斯·布拉加写道,我需要用class
来做它&#34;反应方式&#34;。我没有看到如何包装你的函数并返回一个带有状态的类是&#34;使用类&#34;。
我最害怕的一个问题是,当软件变得越来越复杂并且我们需要处理越来越多的交叉问题时,作为参数处理每个问题将变得越来越难。在这种情况下,最好使用解构模式来解决您需要关注的问题&#34; obejct作为第二个参数传递。
关于这种模式的最后一件事。我做了一个小测试(只是selenium渲染一个页面100次),这个模式,小规模,比使用Redux更快。您的redux状态越大,连接的组件越多,此模式变得越快。不利的一面是,您现在正在进行一些手动状态管理,这会带来复杂的实际成本。记住要权衡所有选项。
与用户互动的应用程序要求您尝试跟踪他们的交互。这些交互可以用不同的方式建模,但我真的很喜欢有状态的方法。这意味着你的线程&#39;通过您的申请表明。现在,您可以通过几种方式创建组件。我想提到的三个是:
Component
PureComponent
我真的很喜欢最后一个选项,但说实话,保持代码performant会很痛苦。我们在那里有一篇 lot 文章解释了每次调用组件时lambda表达式如何创建一个新函数,打破了由props
完成的PureComponent
的浅层比较。
为了抵消这种情况,我使用了一种模式,我将无状态组件包装在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);
答案 2 :(得分:0)
1-这种模式是否会损害性能?
性能通常不是黑/白,而是在不同情况下更好/更差。由于React已经有了这样做的标准方法,因此你可能会错过内部优化。
2 - 这个模式会破坏像shouldComponentUpdate这样的东西吗? (我有一种下沉的感觉,这是建模旧的上下文api)
是的,如果需要编写shouldComponentUpdate函数
,则应该使用类声明3-我有多担心未来的反应更新是否会破坏此代码?
我认为你应该这样说是公平的,因为有明显的,有记录的方法可以使用类来做同样的事情。
4 - 是否有更多的“Reacty”方法在Pure函数中使用State而不使用像Redux这样的库?
你可以拥有一个容器组件,它具有状态并传递回调函数来更新状态
5-我试图解决一些不应该解决的问题吗?
是,因为已经有一种主流和记录的方法可以使用Component类来满足您的需求。您应该只为非常简单或表达的组件使用功能组件