Den*_*ush 126 reactjs reactjs-flux
我来自角度世界,在那里我可以将逻辑提取到服务/工厂并在我的控制器中使用它们.
我试图了解如何在React应用程序中实现相同的功能.
假设我有一个验证用户密码输入的组件(它的强度).它的逻辑非常复杂,因此我不想在它自己的组件中编写它.
我应该在哪里写这个逻辑?在商店里,如果我使用助焊剂?或者有更好的选择吗?
Woj*_*ski 70
当您意识到Angular服务只是一个提供一组与上下文无关的方法的对象时,问题变得非常简单.它只是Angular DI机制,使它看起来更复杂.DI很有用,因为它负责为您创建和维护实例,但您并不真正需要它.
考虑一个名为axios的流行AJAX库(您可能已经听说过):
import axios from "axios";
axios.post(...);
Run Code Online (Sandbox Code Playgroud)
它不是一种服务吗?它提供了一组负责某些特定逻辑的方法,并且与主代码无关.
您的示例案例是关于创建一组用于验证输入的隔离方法(例如,检查密码强度).有人建议将这些方法放在组件中,这对我来说显然是一种反模式.如果验证涉及制作和处理XHR后端调用或进行复杂计算,该怎么办?您会将此逻辑与鼠标单击处理程序和其他UI特定的东西混合使用吗?废话.与容器/ HOC方法相同.包装组件只是为了添加一个方法来检查值中是否有数字?来吧.
我只想创建一个名为'ValidationService.js'的新文件,并按如下方式组织它:
const ValidationService = {
firstValidationMethod: function(value) {
//inspect the value
},
secondValidationMethod: function(value) {
//inspect the value
}
};
export default ValidationService;
Run Code Online (Sandbox Code Playgroud)
然后在你的组件中:
import ValidationService from "./services/ValidationService.js";
...
//inside the component
yourInputChangeHandler(event) {
if(!ValidationService.firstValidationMethod(event.target.value) {
//show a validation warning
return false;
}
//proceed
}
Run Code Online (Sandbox Code Playgroud)
在任何您想要的地方使用此服务.如果验证规则发生更改,则只需关注ValidationService.js文件.
您可能需要更复杂的服务,这取决于其他服务.在这种情况下,您的服务文件可能返回类构造函数而不是静态对象,因此您可以在组件中自己创建对象的实例.您还可以考虑实现一个简单的单例,以确保在整个应用程序中始终只使用一个服务对象实例.
aph*_*ine 46
第一个答案并不反映当前的Container vs Presenter范例.
如果你需要做一些事情,比如验证密码,你可能会有一个功能来完成它.您将该功能作为道具传递给您的可重用视图.
因此,正确的方法是编写一个ValidatorContainer,它将该函数作为属性,并将表单包装在其中,将正确的props传递给子.在您的视图中,验证器容器包装您的视图,视图使用容器逻辑.
验证可以在容器的属性中完成,但是您使用的是第三方验证器或任何简单的验证服务,您可以将该服务用作容器组件的属性并在容器的方法中使用它.我已经为宁静的组件做了这个,它运行得很好.
如果需要更多配置,您可以使用提供者/消费者模型.提供程序是一个高级组件,它包装在顶部应用程序对象(您安装的对象)附近和下方的某个位置,并将其自身的一部分或顶层中配置的属性提供给上下文API.然后我设置我的容器元素以使用上下文.
父/子上下文关系不必彼此靠近,只要孩子必须以某种方式下降.Redux以这种方式存储和React Router功能.我用它来为我的休息容器提供一个root restful上下文(如果我不提供我自己的容器).
(注意:上下文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
};Run Code Online (Sandbox Code Playgroud)
我还没有尝试过,但看过用过的另一种方法是将中间件与Redux结合使用.您可以在应用程序之外定义服务对象,或者至少高于redux存储.在存储创建期间,您将服务注入中间件,中间件处理影响服务的任何操作.
通过这种方式,我可以将restful.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)
}
}Run Code Online (Sandbox Code Playgroud)
Jak*_*oby 29
请记住,React的目的是更好地结合逻辑上应该耦合的东西.如果您正在设计一个复杂的"验证密码"方法,它应该耦合在哪里?
那么每次用户需要输入新密码时你都需要使用它.这可以在注册屏幕上,"忘记密码"屏幕,管理员"重置另一个用户的密码"屏幕等.
但在任何一种情况下,它总是与某些文本输入字段相关联.这就是它应该耦合的地方.
创建一个非常小的React组件,它只包含一个输入字段和相关的验证逻辑.在所有可能想要输入密码的表单中输入该组件.
它与逻辑服务/工厂的结果基本相同,但是您将它直接耦合到输入.因此,您现在永远不需要告诉该函数在哪里查找它的验证输入,因为它永久地绑在一起.
Kil*_*are 24
我需要在多个组件之间共享一些格式化逻辑,而Angular开发人员也自然倾向于服务.
我把它放在一个单独的文件中来共享逻辑
function format(input) {
//convert input to output
return output;
}
module.exports = {
format: format
};
Run Code Online (Sandbox Code Playgroud)
然后将其作为模块导入
import formatter from '../services/formatter.service';
//then in component
render() {
return formatter.format(this.props.data);
}
Run Code Online (Sandbox Code Playgroud)
cor*_*lla 10
同样的情况:完成了多个Angular项目并转向React,没有通过DI提供服务的简单方法似乎是一个缺失的部分(除了服务的细节).
使用上下文和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经验.考虑逻辑的本质:
有些人还可以将HOC用于重复使用,但对我来说,上述内容几乎涵盖了所有用例.此外,考虑使用ducks来扩展状态管理,以使问题保持独立并以状态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"));Run Code Online (Sandbox Code Playgroud)
<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>Run Code Online (Sandbox Code Playgroud)
这是一个简单的例子:
我也来自 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>
)
Run Code Online (Sandbox Code Playgroud)
然后编写一个 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} />
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在这些 HOC 共享相同的验证行为:
const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)
render((
<div>
<InputWithValidator />
<TextAreaWithValidator />
</div>
), document.getElementById('root'));
Run Code Online (Sandbox Code Playgroud)
我创建了一个简单的演示。
编辑:另一个演示是使用 props 传递函数数组,以便您可以跨HOCs共享由多个验证函数组成的逻辑,例如:
<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />
Run Code Online (Sandbox Code Playgroud)
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
}
}
Run Code Online (Sandbox Code Playgroud)
https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js
| 归档时间: |
|
| 查看次数: |
68262 次 |
| 最近记录: |