리액트 스테이트리스 컴포넌트 이벤트핸들러
React stateless 구성 요소에서 이벤트 핸들러를 만드는 최적의 방법을 찾고 있습니다.난 이렇게 할 수 있어:
const myComponent = (props) => {
const myHandler = (e) => props.dispatch(something());
return (
<button onClick={myHandler}>Click Me</button>
);
}
여기서 단점은 이 컴포넌트가 렌더링될 때마다 새로운 "myHandler" 기능이 생성된다는 것입니다.컴포넌트 속성에 액세스할 수 있는 스테이트리스 컴포넌트에 이벤트핸들러를 작성하는 더 좋은 방법이 있을까요?
기능 컴포넌트의 요소에 핸들러를 적용하는 방법은 일반적으로 다음과 같습니다.
const f = props => <button onClick={props.onClick}></button>
더 복잡한 작업이 필요한 경우 a) 컴포넌트가 스테이트리스(클래스 또는 후크 사용)가 되어서는 안 되며 b) 외부 스테이트풀 컨테이너 컴포넌트에 핸들러를 작성해야 합니다.
덧붙여서, 첫 번째 포인트를 약간 손상시키는 것은 컴포넌트가 특별히 집중적으로 재렌더된 앱의 일부에 있지 않는 한, 화살표 기능을 만드는 것에 대해 걱정할 필요가 없습니다.render()
.
새로운 리액트 훅 기능을 사용하면 다음과 같이 됩니다.
const HelloWorld = ({ dispatch }) => {
const handleClick = useCallback(() => {
dispatch(something())
})
return <button onClick={handleClick} />
}
useCallback
메모된 함수를 만듭니다.즉, 각 렌더링 사이클에서 새 함수가 재생성되지 않습니다.
https://reactjs.org/docs/hooks-reference.html#usecallback
그러나, 이것은 아직 제안 단계입니다.
이렇게 하면 어떨까요?
const myHandler = (e,props) => props.dispatch(something());
const myComponent = (props) => {
return (
<button onClick={(e) => myHandler(e,props)}>Click Me</button>
);
}
핸들러가 변경되는 속성에 의존하는 경우 핸들러를 캐시할 스테이트풀인스턴스가 없기 때문에 핸들러를 매번 작성해야 합니다.또 다른 방법은 입력 소품을 기반으로 핸들러를 메모하는 것입니다.
두 가지 구현 옵션 lodash._memoize R.memoize fast-memoize
다음은 리액트 및 레독스 타이프 스크립트로 구현된 간단한 즐겨찾기 제품 목록입니다.커스텀 핸들러에서 필요한 모든 인수를 전달하고 새로운 인수를 반환할 수 있습니다.EventHandler
origin event 인수를 받아들입니다.그건…MouseEvent
를 참조해 주세요.
분리된 기능으로 jsx를 깨끗하게 유지하고 여러 보풀 제거 규칙을 위반하지 않습니다.예를 들어jsx-no-bind
,jsx-no-lambda
.
import * as React from 'react';
import { DispatchProp, Dispatch, connect } from 'react-redux';
import { removeFavorite } from './../../actions/favorite';
interface ListItemProps {
prod: Product;
handleRemoveFavoriteClick: React.EventHandler<React.MouseEvent<HTMLButtonElement>>;
}
const ListItem: React.StatelessComponent<ListItemProps> = (props) => {
const {
prod,
handleRemoveFavoriteClick
} = props;
return (
<li>
<a href={prod.url} target="_blank">
{prod.title}
</a>
<button type="button" onClick={handleRemoveFavoriteClick}>×</button>
</li>
);
};
const handleRemoveFavoriteClick = (prod: Product, dispatch: Dispatch<any>) =>
(e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault();
dispatch(removeFavorite(prod));
};
interface FavoriteListProps {
prods: Product[];
}
const FavoriteList: React.StatelessComponent<FavoriteListProps & DispatchProp<any>> = (props) => {
const {
prods,
dispatch
} = props;
return (
<ul>
{prods.map((prod, index) => <ListItem prod={prod} key={index} handleRemoveFavoriteClick={handleRemoveFavoriteClick(prod, dispatch)} />)}
</ul>
);
};
export default connect()(FavoriteList);
다음은 javascript 스니펫입니다(타자기본에 익숙하지 않은 경우).
import * as React from 'react';
import { DispatchProp, Dispatch, connect } from 'react-redux';
import { removeFavorite } from './../../actions/favorite';
const ListItem = (props) => {
const {
prod,
handleRemoveFavoriteClick
} = props;
return (
<li>
<a href={prod.url} target="_blank">
{prod.title}
</a>
<button type="button" onClick={handleRemoveFavoriteClick}>×</button>
</li>
);
};
const handleRemoveFavoriteClick = (prod, dispatch) =>
(e) => {
e.preventDefault();
dispatch(removeFavorite(prod));
};
const FavoriteList = (props) => {
const {
prods,
dispatch
} = props;
return (
<ul>
{prods.map((prod, index) => <ListItem prod={prod} key={index} handleRemoveFavoriteClick={handleRemoveFavoriteClick(prod, dispatch)} />)}
</ul>
);
};
export default connect()(FavoriteList);
솔루션 1 mapPropsToHandler 및 event.target.
함수는 js의 객체이므로 속성을 부가할 수 있습니다.
function onChange() { console.log(onChange.list) }
function Input(props) {
onChange.list = props.list;
return <input onChange={onChange}/>
}
이 함수는 속성이 함수에 한 번만 바인딩됩니다.
export function mapPropsToHandler(handler, props) {
for (let property in props) {
if (props.hasOwnProperty(property)) {
if(!handler.hasOwnProperty(property)) {
handler[property] = props[property];
}
}
}
}
소품도 이렇게 받고요.
export function InputCell({query_name, search, loader}) {
mapPropsToHandler(onChange, {list, query_name, search, loader});
return (
<input onChange={onChange}/>
);
}
function onChange() {
let {query_name, search, loader} = onChange;
console.log(search)
}
이 예에서는 event.target과 mapPropsToHandler를 조합하고 있습니다.숫자나 문자열이 아닌 핸들러에만 함수를 부가하는 것이 좋습니다.다음과 같은 DOM Atribute의 도움을 받아 숫자와 문자열을 전달할 수 있습니다.
<select data-id={id}/>
mapPropsToHandler가 아닌
import React, {PropTypes} from "react";
import swagger from "../../../swagger/index";
import {sync} from "../../../functions/sync";
import {getToken} from "../../../redux/helpers";
import {mapPropsToHandler} from "../../../functions/mapPropsToHandler";
function edit(event) {
let {translator} = edit;
const id = event.target.attributes.getNamedItem('data-id').value;
sync(function*() {
yield (new swagger.BillingApi())
.billingListStatusIdPut(id, getToken(), {
payloadData: {"admin_status": translator(event.target.value)}
});
});
}
export default function ChangeBillingStatus({translator, status, id}) {
mapPropsToHandler(edit, {translator});
return (
<select key={Math.random()} className="form-control input-sm" name="status" defaultValue={status}
onChange={edit} data-id={id}>
<option data-tokens="accepted" value="accepted">{translator('accepted')}</option>
<option data-tokens="pending" value="pending">{translator('pending')}</option>
<option data-tokens="rejected" value="rejected">{translator('rejected')}</option>
</select>
)
}
솔루션 2이벤트 위임
솔루션 1을 참조하십시오.입력에서 이벤트 핸들러를 삭제하고 다른 입력을 보유하고 있는 부모에게 전달할 수 있으며 도움말 위임 기술을 통해 event.traget 및 mapPropsToHandler 함수를 다시 사용할 수 있습니다.
스테이트리스 컴포넌트처럼 기능만 추가하면 됩니다.
function addName(){
console.log("name is added")
}
답례에서는 을 리고그그그그 and and and and and and and 라고 부릅니다.onChange={addName}
소품 중 우려되는 기능이 몇 가지밖에 없는 경우 다음을 수행할 수 있습니다.
let _dispatch = () => {};
const myHandler = (e) => _dispatch(something());
const myComponent = (props) => {
if (!_dispatch)
_dispatch = props.dispatch;
return (
<button onClick={myHandler}>Click Me</button>
);
}
더 복잡해지면 보통 수업 컴포넌트로 돌아갑니다.
끊임없는 노력 끝에 마침내 내게 효과가 있었다.
//..src/components/atoms/TestForm/index.tsx
import * as React from 'react';
export interface TestProps {
name?: string;
}
export interface TestFormProps {
model: TestProps;
inputTextType?:string;
errorCommon?: string;
onInputTextChange: React.ChangeEventHandler<HTMLInputElement>;
onInputButtonClick: React.MouseEventHandler<HTMLInputElement>;
onButtonClick: React.MouseEventHandler<HTMLButtonElement>;
}
export const TestForm: React.SFC<TestFormProps> = (props) => {
const {model, inputTextType, onInputTextChange, onInputButtonClick, onButtonClick, errorCommon} = props;
return (
<div>
<form>
<table>
<tr>
<td>
<div className="alert alert-danger">{errorCommon}</div>
</td>
</tr>
<tr>
<td>
<input
name="name"
type={inputTextType}
className="form-control"
value={model.name}
onChange={onInputTextChange}/>
</td>
</tr>
<tr>
<td>
<input
type="button"
className="form-control"
value="Input Button Click"
onClick={onInputButtonClick} />
</td>
</tr>
<tr>
<td>
<button
type="submit"
value='Click'
className="btn btn-primary"
onClick={onButtonClick}>
Button Click
</button>
</td>
</tr>
</table>
</form>
</div>
);
}
TestForm.defaultProps ={
inputTextType: "text"
}
//========================================================//
//..src/components/atoms/index.tsx
export * from './TestForm';
//========================================================//
//../src/components/testpage/index.tsx
import * as React from 'react';
import { TestForm, TestProps } from '@c2/component-library';
export default class extends React.Component<{}, {model: TestProps, errorCommon: string}> {
state = {
model: {
name: ""
},
errorCommon: ""
};
onInputTextChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const field = event.target.name;
const model = this.state.model;
model[field] = event.target.value;
return this.setState({model: model});
};
onInputButtonClick = (event: React.MouseEvent<HTMLInputElement>) => {
event.preventDefault();
if(this.validation())
{
alert("Hello "+ this.state.model.name + " from InputButtonClick.");
}
};
onButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();
if(this.validation())
{
alert("Hello "+ this.state.model.name+ " from ButtonClick.");
}
};
validation = () => {
this.setState({
errorCommon: ""
});
var errorCommonMsg = "";
if(!this.state.model.name || !this.state.model.name.length) {
errorCommonMsg+= "Name: *";
}
if(errorCommonMsg.length){
this.setState({ errorCommon: errorCommonMsg });
return false;
}
return true;
};
render() {
return (
<TestForm model={this.state.model}
onInputTextChange={this.onInputTextChange}
onInputButtonClick={this.onInputButtonClick}
onButtonClick={this.onButtonClick}
errorCommon={this.state.errorCommon} />
);
}
}
//========================================================//
//../src/components/home2/index.tsx
import * as React from 'react';
import TestPage from '../TestPage/index';
export const Home2: React.SFC = () => (
<div>
<h1>Home Page Test</h1>
<TestPage />
</div>
);
주의: 텍스트 박스 파일 바인딩의 "name" 속성과 "filename name"(model.name 등)은 동일해야 하며 "onInputTextChange"만 작동합니다."onInputTextChange" 로직은 코드로 변경할 수 있습니다.
이런 건 어때?
let __memo = null;
const myHandler = props => {
if (!__memo) __memo = e => props.dispatch(something());
return __memo;
}
const myComponent = props => {
return (
<button onClick={myHandler(props)}>Click Me</button>
);
}
그러나 이 예시와 같이 onClick을 하위/내부 컴포넌트에 전달할 필요가 없다면 이는 오버킬입니다.
언급URL : https://stackoverflow.com/questions/39260595/event-handlers-in-react-stateless-components
'sourcetip' 카테고리의 다른 글
PowerShell을 사용하여 JSON 파일을 업데이트하는 방법 (0) | 2023.02.17 |
---|---|
리액트 리덕스: 리덕터에 논리가 포함되어 있는지 여부 (0) | 2023.02.17 |
여러 고유 ID를 사용한 Rest API 설계 (0) | 2023.02.17 |
명령줄에서 Mac에 JQ를 설치하려면 어떻게 해야 합니까? (0) | 2023.02.17 |
JSON 데이터의 항목 카운트 방법 (0) | 2023.02.17 |