나는 서비스 / 공장으로 로직을 추출하여 컨트롤러에서 사용할 수있는 각진 세계에서 왔습니다.
React 응용 프로그램에서 어떻게 달성 할 수 있는지 이해하려고합니다.
사용자의 암호 입력을 확인하는 구성 요소가 있다고 가정 해 봅시다 (강점). 논리는 매우 복잡하므로 자체 구성 요소에 쓰지 않으려 고합니다.
이 논리를 어디에 써야합니까? 플럭스를 사용하는 경우 매장에서? 아니면 더 나은 옵션이 있습니까?
나는 서비스 / 공장으로 로직을 추출하여 컨트롤러에서 사용할 수있는 각진 세계에서 왔습니다.
React 응용 프로그램에서 어떻게 달성 할 수 있는지 이해하려고합니다.
사용자의 암호 입력을 확인하는 구성 요소가 있다고 가정 해 봅시다 (강점). 논리는 매우 복잡하므로 자체 구성 요소에 쓰지 않으려 고합니다.
이 논리를 어디에 써야합니까? 플럭스를 사용하는 경우 매장에서? 아니면 더 나은 옵션이 있습니까?
답변:
첫 번째 답변은 현재 컨테이너 대 발표자 패러다임을 반영하지 않습니다 .
암호 확인과 같은 작업을 수행해야 할 경우 암호를 사용하는 기능이있을 수 있습니다. 해당 기능을 소품으로 재사용 가능한 뷰에 전달합니다.
따라서 올바른 방법은 ValidatorContainer를 작성하는 것입니다. ValidatorContainer는 해당 기능을 속성으로 사용하고 양식을 감싸서 올바른 소품을 자식에게 전달합니다. 뷰와 관련하여 유효성 검사기 컨테이너는 뷰를 래핑하고 뷰는 컨테이너 논리를 사용합니다.
유효성 검사는 모두 컨테이너 속성에서 수행 할 수 있지만 타사 유효성 검사기 또는 간단한 유효성 검사 서비스를 사용하는 경우 컨테이너 구성 요소의 속성으로 서비스를 사용하여 컨테이너의 메서드에서 사용할 수 있습니다. 편안한 구성 요소에 대해이 작업을 수행했으며 매우 잘 작동합니다.
구성이 조금 더 필요한 경우 공급자 / 소비자 모델을 사용할 수 있습니다. 공급자는 최상위 응용 프로그램 개체 (마운트 된 개체)에 가깝거나 그 아래에있는 고급 구성 요소로, 자체의 일부 또는 최상위 계층에 구성된 속성을 컨텍스트 API에 제공합니다. 그런 다음 컨텍스트를 사용하도록 컨테이너 요소를 설정했습니다.
부모 / 자녀 관계는 서로 가까이있을 필요는 없으며, 자식은 어떤 식 으로든 내림차순이어야합니다. Redux는 이런 식으로 React Router 기능을 저장합니다. 나는 그것을 내 나머지 컨테이너에 대한 루트 편안한 컨텍스트를 제공하기 위해 사용했다 (내 자신을 제공하지 않으면).
(참고 : 컨텍스트 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)
}
}
Angular 서비스가 컨텍스트 독립적 인 메소드 세트를 제공하는 객체 일 뿐이라는 것을 알면 문제가 매우 간단 해집니다. 더 복잡해 보이는 Angular DI 메커니즘 일뿐입니다. DI는 인스턴스 생성 및 유지 관리를 담당하지만 실제로는 필요하지 않으므로 유용합니다.
axios라는 유명한 AJAX 라이브러리를 생각해 보자.
import axios from "axios";
axios.post(...);
서비스로 동작하지 않습니까? 특정 로직을 담당하는 메소드 세트를 제공하며 기본 코드와 독립적입니다.
귀하의 사례는 입력을 검증하기위한 격리 된 메소드 세트를 작성하는 것입니다 (예 : 비밀번호 강도 확인). 일부는 이러한 방법을 구성 요소 안에 넣을 것을 제안했습니다. 유효성 검사에 XHR 백엔드 호출을 만들고 처리하거나 복잡한 계산을 수행하면 어떻게됩니까? 이 로직을 마우스 클릭 핸들러 및 기타 UI 관련 항목과 혼합 하시겠습니까? 무의미한 말. 컨테이너 / HOC 접근 방식과 동일합니다. 값에 숫자가 있는지 확인하는 방법을 추가하기 위해 구성 요소를 래핑합니까? 어서
'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 파일에만 집중해야합니다.
다른 서비스에 따라 더 복잡한 서비스가 필요할 수 있습니다. 이 경우 서비스 파일은 정적 객체 대신 클래스 생성자를 반환하여 구성 요소에서 객체의 인스턴스를 직접 만들 수 있습니다. 전체 응용 프로그램에서 항상 하나의 서비스 개체 인스턴스 만 사용되도록하기 위해 간단한 싱글 톤 구현을 고려할 수도 있습니다.
여러 구성 요소에서 공유하고 Angular 개발자가 자연스럽게 서비스에 의존 할 때 몇 가지 형식 논리가 필요했습니다.
논리를 별도의 파일에 넣어서 공유했습니다.
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);
}
React의 목적은 논리적으로 결합해야 할 것들을 더 잘 연결하는 것입니다. 복잡한 "유효한 비밀번호"방법을 설계하는 경우 어디에 연결해야합니까?
사용자가 새 암호를 입력해야 할 때마다 암호를 사용해야합니다. 등록 화면, "암호 분실"화면, 관리자 "다른 사용자의 암호 재설정"화면 등일 수 있습니다.
그러나 어떤 경우에도 항상 텍스트 입력 필드에 연결됩니다. 그것이 결합되어야하는 곳입니다.
입력 필드와 관련 검증 로직으로 만 구성된 매우 작은 React 구성 요소를 작성하십시오. 암호를 입력하려는 모든 양식 내에 해당 구성 요소를 입력하십시오.
논리에 대한 서비스 / 공장을 갖는 것과 본질적으로 동일한 결과이지만 입력에 직접 결합합니다. 따라서 이제는 해당 함수가 유효성 검증 입력을 어디에서 찾아야하는지 알 필요가 없습니다. 영구적으로 묶여 있기 때문입니다.
또한 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"));
<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>
다음은 간단한 예입니다.
동일한 상황 : 여러 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 에 도달 하지만 위의 내용은 거의 모든 사용 사례를 다룹니다. 또한 우려를 분리하고 상태 중심 UI를 유지하기 위해 오리 를 사용하여 상태 관리를 확장하는 것을 고려하십시오 .
나는 Angular에서도 왔으며 React를 시험해 보았습니다. 현재 권장되는 방법은 High-Order Components를 사용하는 것 같습니다 .
고차 컴포넌트 (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'));
간단한 데모를 만들었습니다 .
편집 : 또 다른 데모 는 소품을 사용하여 여러 함수를 전달하여 여러 유효성 검사 함수로 구성된 논리를 다음 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
HOC
. 다른 데모에 대한 편집 내용을 참조하십시오.
서비스는 Angular2 + 에서도 Angular로 제한되지 않습니다 .
서비스는 단지 도우미 기능의 모음입니다 ...
그리고 여러 가지 방법으로 애플리케이션을 작성하고 재사용 할 수 있습니다.
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) 함수와 유틸리티가 공유되지 않으면이 경우 반응 구성 요소의 함수와 마찬가지로 React 구성 요소에 넣을 수도 있습니다 ...
class Greeting extends React.Component {
getName() {
return "Alireza Dezfoolian";
}
render() {
return <h1>Hello, {this.getName()}</h1>;
}
}
4) 다른 방법으로 처리 할 수 있고 Redux를 사용할 수 있습니다 . 임시 저장소이므로 React 응용 프로그램 에있는 경우 많은 getter setter 함수 를 사용하는 데 도움이 될 수 있습니다 을 사용하는 있습니다 ... 큰 상점과 같습니다. 상태를 추적하고 구성 요소간에 공유 할 수 있으므로 서비스에서 사용하는 게터 세터에 대한 많은 고통을 제거 할 수 있습니다 ...
항상 DRY 코드 를 작성하고 코드를 재사용하고 읽을 수 있도록하는 데 필요한 것을 반복 하지 않는 것이 좋지만 항목 4에 언급 된대로 Reux 앱에서 각도 방식을 따르지 마십시오. Redux를 사용하면 서비스 및 항목 1과 같은 일부 재사용 가능한 도우미 기능에 대한 사용을 제한합니다.
또는 클래스 상속 "http"를 React Component에 삽입 할 수 있습니다.
소품 개체를 통해.
업데이트 :
ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
다음과 같이 React Component ReactApp를 간단히 편집하십시오.
class ReactApp extends React.Component {
state = {
data: ''
}
render(){
return (
<div>
{this.props.data.getData()}
</div>
)
}
}
내가 찾은 재사용 가능한 논리에 가장 많이 사용되는 패턴은 후크를 작성하거나 utils 파일을 작성하는 것입니다. 그것은 당신이 달성하고자하는 것에 달려 있습니다.
hooks/useForm.js
양식 데이터의 유효성을 검사하려는 경우 useForm.js 라는 사용자 지정 후크를 만들고 양식 데이터를 제공하고 두 가지를 포함하는 객체를 반환합니다.
Object: {
value,
error,
}
진행하면서 더 많은 것을 반환 할 수 있습니다.
utils/URL.js
또 다른 예는 URL에서 일부 정보를 추출하고 함수가 포함 된 utils 파일을 만들고 필요한 곳에서 가져 오는 것입니다.
export function getURLParam(p) {
...
}