我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
当前回答
我也来自Angular,正在尝试React,目前,一个推荐的方法似乎是使用高阶组件:
高阶组件(HOC)是React中的一种高级技术 重用组件逻辑。hoc本身不是React API的一部分。 它们是React的组合特性中出现的一种模式。
假设你有input和textarea,想要应用相同的验证逻辑:
const Input = (props) => (
<input type="text"
style={props.style}
onChange={props.onChange} />
)
const TextArea = (props) => (
<textarea rows="3"
style={props.style}
onChange={props.onChange} >
</textarea>
)
然后编写一个HOC来验证并设置包装组件的样式:
function withValidator(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props)
this.validateAndStyle = this.validateAndStyle.bind(this)
this.state = {
style: {}
}
}
validateAndStyle(e) {
const value = e.target.value
const valid = value && value.length > 3 // shared logic here
const style = valid ? {} : { border: '2px solid red' }
console.log(value, valid)
this.setState({
style: style
})
}
render() {
return <WrappedComponent
onChange={this.validateAndStyle}
style={this.state.style}
{...this.props} />
}
}
}
现在这些hoc共享相同的验证行为:
const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)
render((
<div>
<InputWithValidator />
<TextAreaWithValidator />
</div>
), document.getElementById('root'));
我创建了一个简单的演示。
编辑:另一个演示是使用props来传递一个函数数组,这样你就可以在hoc之间共享由多个验证函数组成的逻辑,比如:
<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />
Edit2: React 16.8+提供了一个新特性Hook,这是另一种共享逻辑的好方法。
const Input = (props) => {
const inputValidation = useInputValidation()
return (
<input type="text"
{...inputValidation} />
)
}
function useInputValidation() {
const [value, setValue] = useState('')
const [style, setStyle] = useState({})
function handleChange(e) {
const value = e.target.value
setValue(value)
const valid = value && value.length > 3 // shared logic here
const style = valid ? {} : { border: '2px solid red' }
console.log(value, valid)
setStyle(style)
}
return {
value,
style,
onChange: handleChange
}
}
https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js
其他回答
或者你可以将类继承“http”注入到React组件中
通过道具对象。
更新: ReactDOM。render(<ReactApp data={app} />, document.getElementById('root')); 简单地像这样编辑React组件ReactApp: 类ReactApp扩展了React。组件{ 状态= { 数据:“ } 呈现(){ 回报( < div > {this.props.data.getData ()} < / div > ) } }
如果你还在寻找像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.js领域,React.js中的服务和工厂更简单。
你可以像我一样使用普通的函数或类,回调样式和事件Mobx:)
// Here we have Service class > dont forget that in JS class is Function class HttpService { constructor() { this.data = "Hello data from HttpService"; this.getData = this.getData.bind(this); } getData() { return this.data; } } // Making Instance of class > it's object now const http = new HttpService(); // Here is React Class extended By React class ReactApp extends React.Component { state = { data: "" }; componentDidMount() { const data = http.getData(); this.setState({ data: data }); } render() { return <div>{this.state.data}</div>; } } ReactDOM.render(<ReactApp />, document.getElementById("root")); <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>JS Bin</title> </head> <body> <div id="root"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> </body> </html>
这里有一个简单的例子:
可以使用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 <></>
}