[reactjs] Having services in React application

I'm coming from the angular world where I could extract logic to a service/factory and consume them in my controllers.

I'm trying to understand how can I achieve the same in a React application.

Let's say that I have a component that validates user's password input (it's strength). It's logic is pretty complex hence I don't want to write it in the component it self.

Where should I write this logic? In a store if I'm using flux? Or is there a better option?

This question is related to reactjs reactjs-flux

The answer is


I also came from Angular.js area and the services and factories in React.js are more simple.

You can use plain functions or classes, callback style and event Mobx like me :)

_x000D_
_x000D_
// Here we have Service class > dont forget that in JS class is Function_x000D_
class HttpService {_x000D_
  constructor() {_x000D_
    this.data = "Hello data from HttpService";_x000D_
    this.getData = this.getData.bind(this);_x000D_
  }_x000D_
_x000D_
  getData() {_x000D_
    return this.data;_x000D_
  }_x000D_
}_x000D_
_x000D_
_x000D_
// Making Instance of class > it's object now_x000D_
const http = new HttpService();_x000D_
_x000D_
_x000D_
// Here is React Class extended By React_x000D_
class ReactApp extends React.Component {_x000D_
  state = {_x000D_
    data: ""_x000D_
  };_x000D_
_x000D_
  componentDidMount() {_x000D_
    const data = http.getData();_x000D_
_x000D_
    this.setState({_x000D_
      data: data_x000D_
    });_x000D_
  }_x000D_
_x000D_
  render() {_x000D_
    return <div>{this.state.data}</div>;_x000D_
  }_x000D_
}_x000D_
_x000D_
ReactDOM.render(<ReactApp />, document.getElementById("root"));
_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>_x000D_
<!DOCTYPE html>_x000D_
<html>_x000D_
<head>_x000D_
  <meta charset="utf-8">_x000D_
  <meta name="viewport" content="width=device-width">_x000D_
  <title>JS Bin</title>_x000D_
</head>_x000D_
<body>_x000D_
  _x000D_
  <div id="root"></div>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>_x000D_
_x000D_
</body>_x000D_
</html>
_x000D_
_x000D_
_x000D_

Here is simple example :


The issue becomes extremely simple when you realize that an Angular service is just an object which delivers a set of context-independent methods. It's just the Angular DI mechanism which makes it look more complicated. The DI is useful as it takes care of creating and maintaining instances for you but you don't really need it.

Consider a popular AJAX library named axios (which you've probably heard of):

import axios from "axios";
axios.post(...);

Doesn't it behave as a service? It provides a set of methods responsible for some specific logic and is independent from the main code.

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.

I would just create a new file named say 'ValidationService.js' and organize it as follows:

const ValidationService = {
    firstValidationMethod: function(value) {
        //inspect the value
    },

    secondValidationMethod: function(value) {
        //inspect the value
    }
};

export default ValidationService;

Then in your component:

import ValidationService from "./services/ValidationService.js";

...

//inside the component
yourInputChangeHandler(event) {

    if(!ValidationService.firstValidationMethod(event.target.value) {
        //show a validation warning
        return false;
    }
    //proceed
}

Use this service from anywhere you want. If the validation rules change you need to focus on the ValidationService.js file only.

You may need a more complicated service which depends on other services. In this case your service file may return a class constructor instead of a static object so you can create an instance of the object by yourself in the component. You may also consider implementing a simple singleton for making sure that there is always only one instance of the service object in use across the entire application.


Service is not limited to Angular, even in Angular2+,

Service is just collection of helper functions...

And there are many ways to create them and reuse them across the application...

1) They can be all separated function which are exported from a js file, similar as below:

export const firstFunction = () => {
   return "firstFunction";
}

export const secondFunction = () => {
   return "secondFunction";
}
//etc

2) We can also use factory method like, with collection of functions... with ES6 it can be a class rather than a function constructor:

class myService {

  constructor() {
    this._data = null;
  }

  setMyService(data) {
    this._data = data;
  }

  getMyService() {
    return this._data;
  }

}

In this case you need make an instance with new key...

const myServiceInstance = new myService();

Also in this case, each instance has it's own life, so be careful if you want to share it across, in that case you should export only the instance you want...

3) If your function and utils not gonna be shared, you can even put them in React component, in this case, just as function in your react component...

class Greeting extends React.Component {
  getName() {
    return "Alireza Dezfoolian";
  }

  render() {
    return <h1>Hello, {this.getName()}</h1>;
  }
}

4) Another way you may handle things, could be using Redux, it's a temporary store for you, so if you have it in your React application, it can help you with many getter setter functions you use... It's like a big store that keep tracks of your states and can share it across your components, so can get rid of many pain for getter setter stuffs we use in the services...

It's always good to do a DRY code and not repeating what needs to be used to make the code reusable and readable, but don't try to follow Angular ways in React app, as mentioned in item 4, using Redux can reduces your need of services and you limit using them for some reuseable helper functions like item 1...


Well the most used pattern for reusable logic I have come across is either writing a hook or creating a utils file. It depends on what you want to accomplish.

hooks/useForm.js

Like if you want to validate form data then I would create a custom hook named useForm.js and provide it form data and in return it would return me an object containing two things:

Object: {
    value,
    error,
}

You can definitely return more things from it as you progress.

utils/URL.js

Another example would be like you want to extract some information from a URL then I would create a utils file for it containing a function and import it where needed:

 export function getURLParam(p) {
...
}

I am in the same boot like you. In the case you mention, I would implement the input validation UI component as a React component.

I agree the implementation of the validation logic itself should (must) not be coupled. Therefore I would put it into a separate JS module.

That is, for logic that should not be coupled use a JS module/class in separate file, and use require/import to de-couple the component from the "service".

This allows for dependency injection and unit testing of the two independently.


Same situation: Having done multiple Angular projects and moving to React, not having a simple way to provide services through DI seems like a missing piece (the particulars of the service aside).

Using context and ES7 decorators we can come close:

https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/

Seems these guys have taken it a step further / in a different direction:

http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs

Still feels like working against the grain. Will revisit this answer in 6 months time after undertaking a major React project.

EDIT: Back 6 months later with some more React experience. Consider the nature of the logic:

  1. Is it tied (only) to UI? Move it into a component (accepted answer).
  2. Is it tied (only) to state management? Move it into a thunk.
  3. Tied to both? Move to separate file, consume in component through a selector and in thunks.

Some also reach for HOCs for reuse but for me the above covers almost all use cases. Also, consider scaling state management using ducks to keep concerns separate and state UI-centric.


Keep in mind that the purpose of React is to better couple things that logically should be coupled. If you're designing a complicated "validate password" method, where should it be coupled?

Well you're going to need to use it every time the user needs to input a new password. This could be on the registration screen, a "forgot password" screen, an administrator "reset password for another user" screen, etc.

But in any of those cases, it's always going to be tied to some text input field. So that's where it should be coupled.

Make a very small React component that consists solely of an input field and the associated validation logic. Input that component within all of the forms that might want to have a password input.

It's essentially the same outcome as having a service/factory for the logic, but you're coupling it directly to the input. So you now never need to tell that function where to look for it's validation input, as it is permanently tied together.


I am from Angular as well and trying out React, as of now, one recommended(?) way seems to be using High-Order Components:

A higher-order component (HOC) is an advanced technique in React for reusing component logic. HOCs are not part of the React API, per se. They are a pattern that emerges from React’s compositional nature.

Let's say you have input and textarea and like to apply the same validation logic:

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>
)

Then write a HOC that does validate and style wrapped component:

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} />
    }
  }
}

Now those HOCs share the same validating behavior:

const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)

render((
  <div>
    <InputWithValidator />
    <TextAreaWithValidator />
  </div>
), document.getElementById('root'));

I created a simple demo.

Edit: Another demo is using props to pass an array of functions so that you can share logic composed by multiple validating functions across HOCs like:

<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />

Edit2: React 16.8+ provides a new feature, Hook, another nice way to share logic.

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


I needed some formatting logic to be shared across multiple components and as an Angular developer also naturally leaned towards a service.

I shared the logic by putting it in a separate file

function format(input) {
    //convert input to output
    return output;
}

module.exports = {
    format: format
};

and then imported it as a module

import formatter from '../services/formatter.service';

//then in component

    render() {

        return formatter.format(this.props.data);
    }

or you can inject the class inheritance "http" into React Component

via props object.

  1. update :

    ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
    
  2. Simply edit React Component ReactApp like this:

    class ReactApp extends React.Component {
    
    state = {
    
        data: ''
    
    }
    
        render(){
    
        return (
            <div>
            {this.props.data.getData()}      
            </div>
    
        )
        }
    }