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


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

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

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

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

10 57

10 ответов:

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

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

Тара

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

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

поставщики

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

отношения контекста родитель/ребенок не должны быть рядом друг с другом, просто ребенок должен быть спущен в некоторых путь. Магазины Redux и реагировать функции маршрутизатора на этом пути. Я использовал его для предоставления корневого контекста 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
};

Middleware

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

таким образом, я мог бы придать моим успокоительным.JS-объект, в middleware и заменить мои методы контейнера с самостоятельными действиями. Мне все равно нужен компонент контейнера для предоставления действий в слой представления формы, но 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. это связана (только) для пользовательского интерфейса? Переместите его в компонент (принятый ответ).
  2. это привязан (только) к государственному управлению? Переместите его в подумать.
  3. привязан к обоим? Перейти к отдельному файлу, потреблять в компоненте через селектор и в преобразователи.

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

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

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

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

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

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

Я бы просто создал новый файл с именем 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) мы можем также использовать метод фабрики, как, с коллекцией функции... с ES6 это может быть класс, а не конструктор функции:

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) другой способ вы можете обрабатывать вещи, может быть с помощью возвращение, это временный магазин для вас, так что если у вас есть это в вашем реагировать приложение, он может помочь вам со многими getter setter функции вы используете... Это похоже на большой магазин, который отслеживает ваши состояния и может делиться ими с вашими компонентами, поэтому может избавиться от многих болей для материалов getter setter, которые мы используем услуга...

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

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

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

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

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

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

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

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

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

этот позволяет для инъекции зависимости и модульного тестирования двух независимо.

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

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

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

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

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