Наличие служб в приложении React

Я прихожу из углового мира, где я мог бы извлечь логику на службу / фабрику и потреблять их в своих контроллерах.

Я пытаюсь понять, как я могу достичь того же в приложении реагируют.

предположим, что у меня есть компонент, который проверяет ввод пароля пользователя (это сила). Это логика довольно сложная, поэтому я не хочу писать ее в компоненте IT self.

где я должен написать эту логику? В магазине, если я использую флюс? Или есть ли лучший вариант?

10 ответов


первый ответ не отражает текущего контейнер против ведущий парадигмы.

Если вам нужно что-то сделать, например, проверить пароль, у вас, вероятно, есть функция, которая это делает. Вы передадите эту функцию в свой многоразовый вид в качестве опоры.

Тара

Итак, правильный способ сделать это-написать ValidatorContainer, который будет иметь эту функцию как свойство, и обернуть форму в нее, передавая правильные реквизиты в ребенок. Когда дело доходит до вашего представления, ваш контейнер валидатора обертывает ваше представление, и представление потребляет логику контейнеров.

проверка может быть выполнена в свойствах контейнера, но если вы используете сторонний валидатор или любую простую службу проверки, вы можете использовать службу как свойство компонента контейнера и использовать ее в методах контейнера. Я сделал это для компонентов restful, и он работает очень хорошо.

поставщики

Если там немного больше конфигурации необходимо, вы можете использовать модель поставщика / потребителя. Поставщик-это компонент высокого уровня, который обертывается где-то рядом с верхним объектом приложения (тем, который вы монтируете) и предоставляет часть себя или свойство, настроенное в верхнем слое, контекстному API. Затем я устанавливаю элементы контейнера для использования контекста.

отношения контекста родитель / ребенок не должны быть рядом друг с другом, просто ребенок должен быть спущен в некоторых путь. Redux хранит и функция маршрутизатора React таким образом. Я использовал его для предоставления корневого контекста restful для моих контейнеров 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. Во время создания хранилища вы вводите службу в промежуточное ПО, и промежуточное ПО обрабатывает любые действия, влияющие на службу.

таким образом, я мог бы ввести свой 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)
  }
}

имейте в виду, что цель React-лучше сочетать вещи, которые логически должны быть связаны. Если вы разрабатываете сложный метод "проверки пароля", где он должен быть связан?

Ну, вам нужно будет использовать его каждый раз, когда пользователю нужно ввести новый пароль. Это может быть экран регистрации, экран "забыли пароль", экран администратора" сбросить пароль для другого пользователя " и т. д.

но в любом из этих случаев, это всегда будет привязан к некоторому текстовому полю ввода. Так вот где он должен быть соединен.

сделайте очень маленький компонент React, который состоит исключительно из поля ввода и связанной логики проверки. Введите этот компонент во всех формах, которые могут потребоваться для ввода пароля.

Это по существу тот же результат, что и наличие службы/фабрики для логики, но вы связываете ее непосредственно с входом. Так что теперь вам никогда не нужно говорить этой функции, Где искать ибо это вход проверки, поскольку он постоянно связан вместе.


мне нужна была логика форматирования для совместного использования нескольких компонентов, и как угловой разработчик также, естественно, склонялся к сервису.

я поделился логикой, поместив ее в отдельный файл

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

module.exports = {
    format: format
};

а затем импортировал его как модуль

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

//then in component

    render() {

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

Я также пришел из углового.зона js и обслуживания и фабрики в реагируют.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>

вот простой пример :


та же ситуация: выполнив несколько угловых проектов и перейдя к реагированию, не имея простого способа предоставления услуг через DI, кажется недостающей частью (подробности службы в сторону).

используя контекст и декораторы ES7, мы можем приблизиться:

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

кажется, эти ребята сделали еще один шаг вперед / в другом направление:

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

все еще чувствует, что работает против зерна. Пересмотрит этот ответ через 6 месяцев после проведения крупного проекта React.

EDIT: назад 6 месяцев спустя с еще одним опытом реагирования. Рассмотрим природу логики:

  1. привязан ли он (только) к UI? Переместите его в компонент (принятый ответ).
  2. это привязан (только) к государственному управлению? Переместите его в подумать.
  3. привязан к обоим? Переместить в отдельный файл, потреблять в компоненте через селектор и в преобразователи.

некоторые также достигают HOCs для повторного использования, но для меня выше охватывает практически все случаи использования. Кроме того, рассмотрите возможность масштабирования управления состоянием с помощью утки чтобы сохранить проблемы отдельно и состояние UI-centric.


проблема становится чрезвычайно простой, когда вы понимаете, что угловая служба-это просто объект, который предоставляет набор контекстно-независимых методов. Это просто угловой механизм DI, который делает его более сложным. DI полезен, поскольку он заботится о создании и поддержании экземпляров для вас, но вам это действительно не нужно.

рассмотрим популярную библиотеку AJAX с именем axios (о которой вы, вероятно, слышали):

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

он не ведет себя как сервис? Он предоставляет набор методов, отвечающих за определенную логику и не зависящих от основного кода.

в вашем примере речь шла о создании изолированного набора методов проверки ваших входных данных (например, проверка силы пароля). Некоторые предложили поместить эти методы внутри компонентов, которые для меня явно являются анти-шаблоном. Что делать, если проверка включает в себя выполнение и обработку вызовов XHR backend или выполнение сложных вычислений? Вы бы смешали эту логику с щелчком мыши обработчики и другие специфические вещи UI? Чушь. То же самое с контейнерным/специальным подходом. Обертывание компонента только для добавления метода, который будет проверять, имеет ли значение цифру в нем? Ну же.

Я бы просто создал новый файл с именем say ' ValidationService.Яш и организовать его следующим образом:

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.

вам может понадобиться более сложная услуга, которая зависит от других услуг. В этом случае файл службы может возвращать конструктор класса вместо статического объекта, чтобы вы могли самостоятельно создать экземпляр объекта в компоненте. Вы также можете реализовать простой синглтон, чтобы убедиться, что всегда есть только один экземпляр объекта службы, используемый во всем приложении.


обслуживание не ограничено угловым, даже в Angular2+,

сервис просто набор вспомогательных функций...

и есть много способов их создания и повторного использования в приложении...

1) они могут быть все разделенные функции, которые экспортируются из файла js, как показано ниже:

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

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

2) мы можем также использовать метод фабрики, как, с коллекцией функции... с ЕС6 это может быть класс, а не конструктор функции:

class myService {

  constructor() {
    this._data = null;
  }

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

  getMyService() {
    return this._data;
  }

}

в этом случае вам нужно сделать экземпляр с новым ключом...

const myServiceInstance = new myService();

также в этом случае каждый экземпляр имеет свою собственную жизнь, поэтому будьте осторожны, если вы хотите поделиться им, в этом случае вы должны экспортировать только тот экземпляр, который хотите...

3) если ваша функция и utils не будут разделены, вы даже можете поместить их в компонент React, в этом случай, как раз как функция в вашем компоненте react...

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

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

4) другой способ, которым вы можете обрабатывать вещи, может использовать Redux, это временный магазин для вас, так что если у вас есть это в вашем реагировать приложение, он может помочь вам со многими getter сеттер функции вы используете... Это похоже на большой магазин, который отслеживает ваши состояния и может делиться им с вашими компонентами, поэтому может избавиться от многих болей для геттера сеттера, которые мы используем в услуга...

это всегда хорошо, чтобы сделать сухой код и не повторяя то, что нужно использовать, чтобы сделать код многоразовым и читаемым, но не пытайтесь следовать угловым путям в приложении React, как упоминалось в пункте 4, Использование Redux can уменьшает вашу потребность в услугах, и вы ограничиваете их использование для некоторых повторно используемых вспомогательных функций, таких как пункт 1...


Я тоже из Angular и пытаюсь реагировать, как сейчас, рекомендуется (?) кстати, похоже, использует Компоненты Высокого Порядка:

компонент более высокого порядка (HOC) является передовым методом в React for повторное использование компонентной логики. HOCs не являются частью API React как такового. Они являются паттерном, который возникает из композиционной природы 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'));

Я создал простой демо.

редактировать: еще один демо использует реквизит для передачи массива функций, чтобы вы могли делиться логикой, состоящей из нескольких проверяющих функций через HOCs нравится:

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

Я в том же ботинке, что и ты. В случае, если вы упомянули, я бы реализовал компонент UI проверки ввода как компонент React.

Я согласен, что реализация самой логики проверки не должна (должна) быть связана. Поэтому я бы поместил его в отдельный модуль JS.

то есть для логики, которая не должна быть связана, используйте модуль/класс JS в отдельном файле и используйте require/import для удаления компонента из "службы".

этот учитывает впрыску зависимости и модульное тестирование 2 независимо.


или вы можете ввести наследование класса "http" в компонент React

через объект реквизит.

  1. обновление :

    ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
    
  2. просто отредактируйте React Component ReactApp следующим образом:

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