我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
当前回答
第一个答案没有反映当前的容器vs呈现者范式。
如果你需要做一些事情,比如验证密码,你可能会有一个函数来做这件事。你会把这个函数作为道具传递给你的可重用视图。
容器
因此,正确的方法是编写一个ValidatorContainer,它将该函数作为属性,并将表单包装在其中,将正确的道具传递给子对象。当涉及到视图时,验证器容器包装视图,视图使用容器逻辑。
验证可以在容器的属性中完成,但如果你使用第三方验证器,或任何简单的验证服务,你可以将该服务作为容器组件的属性,并在容器的方法中使用它。我已经为restful组件做过这样的操作,效果非常好。
供应商
如果需要进行更多的配置,则可以使用提供者/消费者模型。提供者是一种高级组件,它包装在顶层应用程序对象(您要挂载的对象)附近和下面的某个地方,并向上下文API提供它自己的一部分,或者在顶层配置的属性。然后,我将容器元素设置为使用上下文。
父/子上下文关系不必彼此接近,只是子上下文关系必须以某种方式继承。Redux以这种方式存储和React Router函数。我已经使用它为我的rest容器提供了一个根rest上下文(如果我没有提供自己的)。
(注意:上下文API在文档中被标记为实验性的,但我认为它不再是实验性的,考虑到使用它的是什么)。
//An example of a Provider component, takes a preconfigured restful.js //object and makes it available anywhere in the application export default class RestfulProvider extends React.Component { constructor(props){ super(props); if(!("restful" in props)){ throw Error("Restful service must be provided"); } } getChildContext(){ return { api: this.props.restful }; } render() { return this.props.children; } } RestfulProvider.childContextTypes = { api: React.PropTypes.object };
中间件
还有一种我还没有尝试过,但有人使用过的方法是将中间件与Redux结合使用。您可以在应用程序之外定义服务对象,或者至少在redux存储区之上定义服务对象。在存储创建过程中,将服务注入中间件,中间件处理影响该服务的任何操作。
通过这种方式,我可以将rest .js对象注入到中间件中,并将容器方法替换为独立的操作。我仍然需要一个容器组件来为表单视图层提供操作,但是connect()和mapDispatchToProps已经覆盖了我。
例如,新的v4 react-router-redux使用这种方法来影响历史记录的状态。
//Example middleware from react-router-redux //History is our service here and actions change it. import { CALL_HISTORY_METHOD } from './actions' /** * This middleware captures CALL_HISTORY_METHOD actions to redirect to the * provided history object. This will prevent these actions from reaching your * reducer or any middleware that comes after this one. */ export default function routerMiddleware(history) { return () => next => action => { if (action.type !== CALL_HISTORY_METHOD) { return next(action) } const { payload: { method, args } } = action history[method](...args) } }
其他回答
或者你可以将类继承“http”注入到React组件中
通过道具对象。
更新: ReactDOM。render(<ReactApp data={app} />, document.getElementById('root')); 简单地像这样编辑React组件ReactApp: 类ReactApp扩展了React。组件{ 状态= { 数据:“ } 呈现(){ 回报( < div > {this.props.data.getData ()} < / div > ) } }
可以使用export关键字从包含必要方法的文件中使用函数。
让我举个例子。假设我们有一个名为someservices .ts的文件:
export const foo = (formId: string) => {
// ... the code is omitted for the brevity
}
export const bar = (): Entity[] => [
// ... the code is omitted for the brevity
]
export default {
foo,
bar,
}
然后我们可以像这样在组件中使用这个服务:
import {
foo,
bar,
} from './someService'
const InnerOrderModal: FC = observer(() => {
const handleFormClick = (value: unknown, item: any) => {
foo(item.key)
bar()
return <></>
}
在React世界中,我们有两种类型的逻辑:有状态的和无状态的。现在,这是开始React时要掌握的主要概念。这里我们更新的状态应该是更新UI,而不是Angular对dom的直接更新。两种类型的逻辑是:
That do not depend on state changes, i.e. static logic which doesn't need to re-render something based on state changes. For such cases just create regular js files and import them like a library or helper methods If you have some code that depends on state and u need to resuse it then two options - hocs and the newer hooks. Hooks are a bit hard to wrap our heads around but basically they would force their parent to rerender if their internal state changes so any stateful logic can be defined and reused in different components, and each hook instance would have its own isolated scope.
理解状态和声明性组件需要一点思维转变,但可以在评论中随意提出后续问题
我和你处境相同。在你提到的情况下,我将输入验证UI组件实现为一个React组件。
我同意验证逻辑本身的实现应该(必须)不耦合。因此,我会把它放在一个单独的JS模块中。
也就是说,对于不应该耦合的逻辑,在单独的文件中使用JS模块/类,并使用require/import将组件与“服务”解耦。
这允许独立地进行依赖注入和单元测试。
请记住,React的目的是更好地耦合逻辑上应该耦合的东西。如果您正在设计一个复杂的“验证密码”方法,它应该耦合在哪里?
每次用户需要输入新密码时,你都需要用到它。这可能出现在注册屏幕、“忘记密码”屏幕、管理员“为其他用户重置密码”屏幕等。
但在这些情况下,它总是会绑定到某个文本输入域。这就是它们应该耦合的地方。
制作一个非常小的React组件,只包含一个输入字段和相关的验证逻辑。在所有可能需要密码输入的表单中输入该组件。
这本质上与为逻辑提供服务/工厂的结果相同,但您将其直接耦合到输入。因此,现在您永远不需要告诉该函数在哪里查找它的验证输入,因为它是永久地绑定在一起的。