我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
当前回答
如果你还在寻找像Angular这样的服务,你可以尝试react-rxbuilder库
您可以使用@Injectable来注册服务,然后您可以使用useService或CountService。在组件中使用服务
import { RxService, Injectable, useService } from "react-rxbuilder";
@Injectable()
export class CountService {
static ins: CountService;
count = 0;
inc() {
this.count++;
}
}
export default function App() {
const [s] = useService(CountService);
return (
<div className="App">
<h1>{s.count}</h1>
<button onClick={s.inc}>inc</button>
</div>
);
}
// Finally use `RxService` in your root component
render(<RxService>{() => <App />}</RxService>, document.getElementById("root"));
预防措施
取决于rxjs和typescript 不能在服务中使用箭头函数
其他回答
当你意识到Angular服务只是一个对象,它交付了一组与上下文无关的方法时,问题就变得极其简单了。只是Angular的DI机制让它看起来更复杂。DI非常有用,因为它负责为您创建和维护实例,但您并不真正需要它。
考虑一个名为axios的流行AJAX库(你可能听说过):
import axios from "axios";
axios.post(...);
它不是一个服务吗?它提供了一组负责某些特定逻辑的方法,并且独立于主代码。
Your example case was about creating an isolated set of methods for validating your inputs (e.g. checking the password strength). Some suggested to put these methods inside the components which for me is clearly an anti-pattern. What if the validation involves making and processing XHR backend calls or doing complex calculations? Would you mix this logic with mouse click handlers and other UI specific stuff? Nonsense. The same with the container/HOC approach. Wrapping your component just for adding a method which will check whether the value has a digit in it? Come on.
我只需要创建一个名为ValidationService.js的新文件,并按照如下方式组织它:
const ValidationService = {
firstValidationMethod: function(value) {
//inspect the value
},
secondValidationMethod: function(value) {
//inspect the value
}
};
export default ValidationService;
然后在你的组件中:
import ValidationService from "./services/ValidationService.js";
...
//inside the component
yourInputChangeHandler(event) {
if(!ValidationService.firstValidationMethod(event.target.value) {
//show a validation warning
return false;
}
//proceed
}
您可以在任何地方使用这项服务。如果验证规则改变了,你只需要关注ValidationService.js文件。
您可能需要一个依赖于其他服务的更复杂的服务。在这种情况下,您的服务文件可能返回一个类构造函数而不是一个静态对象,因此您可以在组件中自己创建该对象的实例。您还可以考虑实现一个简单的单例,以确保在整个应用程序中始终只有一个服务对象实例在使用。
同样的情况:在做了多个Angular项目后转向React,没有一个简单的方法通过DI提供服务似乎是一个缺失的部分(抛开服务的细节不谈)。
使用context和ES7装饰器,我们可以接近:
https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
似乎这些家伙在不同的方向上更进一步:
http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs
还是觉得有违常理。在承担一个主要的React项目后,将在6个月的时间内重新审视这个答案。
编辑:6个月后回来,有了更多的React经验。考虑一下逻辑的本质:
它是否(仅)绑定到UI?将其移动到组件中(已接受的答案)。 它(只)与国家管理有关吗?移动到一个坦克。 两者都有关系?移动到单独的文件,在组件中通过选择器和坦克消费。
有些人还使用hoc进行重用,但对我来说,以上几乎涵盖了所有用例。另外,考虑使用鸭子来扩展状态管理,以保持关注点的独立性和以状态ui为中心。
第一个答案没有反映当前的容器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) } }
我需要一些可以在多个组件间共享的格式化逻辑,而作为Angular开发人员,我自然也倾向于使用服务。
我通过将其放在一个单独的文件中来共享逻辑
function format(input) {
//convert input to output
return output;
}
module.exports = {
format: format
};
然后把它作为一个模块导入
import formatter from '../services/formatter.service';
//then in component
render() {
return formatter.format(this.props.data);
}
或者你可以将类继承“http”注入到React组件中
通过道具对象。
更新: ReactDOM。render(<ReactApp data={app} />, document.getElementById('root')); 简单地像这样编辑React组件ReactApp: 类ReactApp扩展了React。组件{ 状态= { 数据:“ } 呈现(){ 回报( < div > {this.props.data.getData ()} < / div > ) } }