我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。
我试图了解如何在React应用程序中实现相同的功能。
假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。
我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?
当前回答
同样的情况:在做了多个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项目后转向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为中心。
我也来自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>
这里有一个简单的例子:
或者你可以将类继承“http”注入到React组件中
通过道具对象。
更新: ReactDOM。render(<ReactApp data={app} />, document.getElementById('root')); 简单地像这样编辑React组件ReactApp: 类ReactApp扩展了React。组件{ 状态= { 数据:“ } 呈现(){ 回报( < div > {this.props.data.getData ()} < / div > ) } }
我也来自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