我来自angular世界,在那里我可以提取逻辑到服务/工厂,并在我的控制器中使用它们。

我试图了解如何在React应用程序中实现相同的功能。

假设我有一个验证用户密码输入的组件(它的强度)。它的逻辑相当复杂,因此我不想把它写在组件中。

我应该把这个逻辑写在哪里?如果我在商店里使用助焊剂?还是有更好的选择?


当前回答

在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.

理解状态和声明性组件需要一点思维转变,但可以在评论中随意提出后续问题

其他回答

我也来自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 > ) } }

请记住,React的目的是更好地耦合逻辑上应该耦合的东西。如果您正在设计一个复杂的“验证密码”方法,它应该耦合在哪里?

每次用户需要输入新密码时,你都需要用到它。这可能出现在注册屏幕、“忘记密码”屏幕、管理员“为其他用户重置密码”屏幕等。

但在这些情况下,它总是会绑定到某个文本输入域。这就是它们应该耦合的地方。

制作一个非常小的React组件,只包含一个输入字段和相关的验证逻辑。在所有可能需要密码输入的表单中输入该组件。

这本质上与为逻辑提供服务/工厂的结果相同,但您将其直接耦合到输入。因此,现在您永远不需要告诉该函数在哪里查找它的验证输入,因为它是永久地绑定在一起的。

我和你处境相同。在你提到的情况下,我将输入验证UI组件实现为一个React组件。

我同意验证逻辑本身的实现应该(必须)不耦合。因此,我会把它放在一个单独的JS模块中。

也就是说,对于不应该耦合的逻辑,在单独的文件中使用JS模块/类,并使用require/import将组件与“服务”解耦。

这允许独立地进行依赖注入和单元测试。

当你意识到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文件。

您可能需要一个依赖于其他服务的更复杂的服务。在这种情况下,您的服务文件可能返回一个类构造函数而不是一个静态对象,因此您可以在组件中自己创建该对象的实例。您还可以考虑实现一个简单的单例,以确保在整个应用程序中始终只有一个服务对象实例在使用。