Javascript React 无状态组件中的事件处理程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39260595/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Event Handlers in React Stateless Components
提问by aStewartDesign
Trying to figure out an optimal way to create event handlers in React stateless components. I could do something like this:
试图找出在 React 无状态组件中创建事件处理程序的最佳方法。我可以做这样的事情:
const myComponent = (props) => {
const myHandler = (e) => props.dispatch(something());
return (
<button onClick={myHandler}>Click Me</button>
);
}
The drawback here being that every time this component is rendered, a new "myHandler" function is created. Is there a better way to create event handlers in stateless components that can still access the component properties?
这里的缺点是每次渲染这个组件时,都会创建一个新的“myHandler”函数。有没有更好的方法在无状态组件中创建事件处理程序,这些组件仍然可以访问组件属性?
回答by Jed Richards
Applying handlers to elements in function components should generally just look like this:
将处理程序应用于函数组件中的元素通常应该如下所示:
const f = props => <button onClick={props.onClick}></button>
If you need to do anything much more complex it's a sign that either a) the component shouldn't be stateless (use a class, or hooks), or b) you should be creating the handler in an outer stateful container component.
如果您需要做任何更复杂的事情,这表明 a) 组件不应该是无状态的(使用类或钩子),或者 b) 您应该在外部有状态容器组件中创建处理程序。
As an aside, and undermining my first point slightly, unless the component is in a particularly intensively re-rendered part of the app there's no need to worry about creating arrow functions in render()
.
顺便说一句,稍微破坏我的第一点,除非组件位于应用程序的特别密集的重新渲染部分,否则无需担心在render()
.
回答by ryanVincent
Using the new React hooks feature it could look something like this:
使用新的 React hooks 功能,它可能看起来像这样:
const HelloWorld = ({ dispatch }) => {
const handleClick = useCallback(() => {
dispatch(something())
})
return <button onClick={handleClick} />
}
useCallback
creates a memoised function, meaning a new function will not be regenerated on each render cycle.
useCallback
创建一个记忆函数,这意味着不会在每个渲染周期重新生成一个新函数。
https://reactjs.org/docs/hooks-reference.html#usecallback
https://reactjs.org/docs/hooks-reference.html#usecallback
However, this is still at proposal stage.
然而,这仍处于提案阶段。
回答by Phi Nguyen
How about this way :
这种方式怎么样:
const myHandler = (e,props) => props.dispatch(something());
const myComponent = (props) => {
return (
<button onClick={(e) => myHandler(e,props)}>Click Me</button>
);
}
回答by ryanjduffy
If the handler relies on properties that change, you will have to create the handler each time since you lack a stateful instance on which to cache it. Another alternative which may work would be to memoize the handler based on the input props.
如果处理程序依赖于更改的属性,则每次都必须创建处理程序,因为您缺少可以缓存它的有状态实例。另一种可能有效的替代方法是根据输入道具记忆处理程序。
Couple implementation options lodash._memoizeR.memoizefast-memoize
几个实现选项 lodash._memoize R.memoize fast-memoize
回答by Hassan Gilak
solution one mapPropsToHandler and event.target.
解决方案一 mapPropsToHandler 和 event.target。
functions are objects in js so its possible to attach them properties.
函数是 js 中的对象,因此可以附加它们的属性。
function onChange() { console.log(onChange.list) }
function Input(props) {
onChange.list = props.list;
return <input onChange={onChange}/>
}
this function only bind once a property to a function.
这个函数只将一个属性绑定到一个函数。
export function mapPropsToHandler(handler, props) {
for (let property in props) {
if (props.hasOwnProperty(property)) {
if(!handler.hasOwnProperty(property)) {
handler[property] = props[property];
}
}
}
}
I do get my props just like this.
我确实是这样得到我的道具的。
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)
}
this example combined both event.target and mapPropsToHandler. its better to attach functions to handlers only not numbers or strings. number and strings could be passed with help of DOM attribute like
这个例子结合了 event.target 和 mapPropsToHandler。最好将函数附加到处理程序,而不是数字或字符串。数字和字符串可以在 DOM 属性的帮助下传递,例如
<select data-id={id}/>
rather than mapPropsToHandler
而不是 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>
)
}
solution two. event delegation
解决方案二。事件委托
see solution one. we can remove event handler from input and put it to its parent that holds other inputs too and by the help delegation technique we can be use event.traget and mapPropsToHandler function again.
见解决方案一。我们可以从输入中删除事件处理程序,并将其放入也包含其他输入的父级,通过帮助委托技术,我们可以再次使用 event.traget 和 mapPropsToHandler 函数。
回答by jasperjian
Here is my simple favorite products list implemented with react and redux writing in typescript. You can pass all arguments you need in the custom handler and return a new EventHandler
which accepts origin event argument. It's MouseEvent
in this example.
这是我用 React 和 Redux 在打字稿中编写的简单最喜欢的产品列表。您可以在自定义处理程序中传递您需要的所有参数,并返回一个EventHandler
接受原始事件参数的新参数。它MouseEvent
在这个例子中。
Isolated functions keep jsx cleaner and prevent from breaking several linting rules. Such as jsx-no-bind
, jsx-no-lambda
.
隔离的函数使 jsx 更干净,并防止破坏多个 linting 规则。比如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);
Here is the javascript snippet if you are not familiar with typescript:
如果您不熟悉打字稿,这里是 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);
回答by Thulasiram
After continuous effort finally worked for me.
经过不断的努力终于为我工作。
//..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>
);
Note:for text box filed binding "name" attribute & "property name" (e.g: model.name) should be same then only "onInputTextChange" will work. "onInputTextChange" logic can be modified by your code.
注意:对于文本框绑定的“名称”属性和“属性名称”(例如:model.name)应该相同,那么只有“onInputTextChange”可以工作。“onInputTextChange”逻辑可以通过您的代码进行修改。
回答by jslatts
If you only have a few functions in you props that you are worried about you can do this:
如果您担心的道具中只有几个功能,您可以这样做:
let _dispatch = () => {};
const myHandler = (e) => _dispatch(something());
const myComponent = (props) => {
if (!_dispatch)
_dispatch = props.dispatch;
return (
<button onClick={myHandler}>Click Me</button>
);
}
If it gets much more complicated, I usually just go back to having a class component.
如果它变得更复杂,我通常只是回到拥有一个类组件。
回答by Akarsh Srivastava
Like for a stateless component, just add a function -
就像无状态组件一样,只需添加一个函数 -
function addName(){
console.log("name is added")
}
and it is called in the return as onChange={addName}
它在返回中被称为 onChange={addName}
回答by Arnel Enero
How about something like this:
这样的事情怎么样:
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>
);
}
but really this is overkill if you don't need to pass the onClick to lower/inner components, like in the example.
但实际上,如果您不需要将 onClick 传递给下层/内部组件,就像示例中那样,这真的有点过分了。