ReactJS synthetic event for hit-state? - reactjs

I recently did something like this
onHit(functionCall)
{
let attr = {};
attr["onKeyPress"] = functionCall;
attr["onClick"] = functionCall;
attr["onTouchEnd"] = functionCall;
return attr;
}
So that I can do this in my JSX
<a {...this.onHit((e)=>this.doSomething())} title="This has no href" tabIndex="0">This has no href</a>
Everything works as expected, where keypress, click, and touchdown all trigger the same event.
I created this onHit function because I'm building a web application where all action controls need to be accessible by keyboard, mouse and touch screen.
Before I continue using my custom onHit function, is there a more idiomatic way of doing this in ReactJS?

I think your solution is nice, but you could also create a custom component that takes the component/element type as prop, the onHit function as prop and uses that as event handler for all events, and that spreads the rest of the props.
Example
function MyComponent({ element: Element, onHit, ...rest }) {
return (
<Element onKeyPress={onHit} onClick={onHit} onTouchEnd={onHit} {...rest} />
);
}
class App extends React.Component {
render() {
return (
<MyComponent
element={"a"}
onHit={() => console.log("hit!")}
title="This has no href"
tabIndex="0"
>
This has no href
</MyComponent>
);
}
}
ReactDOM.render(<App />, 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>
<div id="root"></div>

Related

React Native: How to focus a TextInput wrapped in a custom component?

In my React Native app I have a custom component that wraps a TextInput. In the code that uses the custom component, I want to call .focus() on the TextInput. I tried calling it directly on the custom component like this:
<CustomComponent
ref={ref => {
this.customComponent = ref;
}}
/>
this.customComponent.focus();
but I can't because, I assume, custom components don't have a focus() method. I could find a way to call it on the wrapped TextInput, but I'm wondering if there's a way to call it directly on the custom component.
Does anyone know how I can approach this?
if a class component you could use CustomComponent.prototype.yourFunction()
youFunction uses ref to call focus() on TextInput
If CustomComponent is your own component then in that case you can pass the ref via Forwarding Refs :
Here I have created sample code snippets, which has input inside CustomComponent and we can access ref inside that component via React.forwardRef((props, ref)
Then we have createRef from parent component and pass ref like <CustomComponent ref={this.ref}>.
Hope this will help you.
const CustomComponent = React.forwardRef((props, ref) => (
<div>
<input type="text" ref={ref} />
</div>
));
class App extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
componentDidMount() {
if(this.ref.current) {
this.ref.current.focus();
}
}
render() {
return (
<div>
Get Focused :
<CustomComponent ref={this.ref} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('react-root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react-root"></div>

How to identify React component tags after build in a CRA app?

I would like to create and use this component in React with ES6:
<Menu>
<MenuHeader>Hi</MenuHeader>
<MenuItem>Hello</MenuItem>
<MenuFooter>End</MenuFooter>
</Menu>
I've defined a component to handle this structure:
export class Menu extends React.Component {
render() {
return (
<div ...>
<div ...>
{HOW TO SELECT HEADER?}
</div>
<div ...>
{HOW TO SELECT ITEM?}
</div>
<div ...>
{HOW TO SELECT FOOTER?}
</div>
</div>
)}
}
It's okay to iterate over children and select by type.name while running on the dev server without transpilation:
{ React.Children.map(this.props.children, child => { return child.props.type === 'MenuItem' ? <>{ child }</> : '' } ) }
But it does not work after building it (cause of uglify/minify process).
For example, Semantic UI React handles it well - but it uses interfaces and written in TypeScript so I cannot use it as reference.
And one more thing (ah Steve:): I do not want to use npm eject.
This is normally done by allowing the compound components inside them to render their own children and Menu would just render the children it gets, hence maintaining the order.
You might want to share the state of things happening between the Header, Body and Footer, so we add a ContextProvider to the Menu component, so they can all share common state.
const rootEl = document.getElementById('root');
const { render } = ReactDOM;
const { createContext } = React;
function MenuHeader({ children }) {
return (
<header className="menu-header">
{children}
</header>
)
}
function MenuBody({ children }) {
return (
<div className="menu-body">
{children}
</div>
)
}
const MenuContext = createContext();
Menu.Header = MenuHeader;
Menu.Body = MenuBody;
function Menu({ children }) {
return (
<MenuContext.Provider value={null}>
<div className="menu-wrapper">
{children}
</div>
</MenuContext.Provider>
);
}
function App() {
return (
<Menu>
<Menu.Header>Menu Header</Menu.Header>
<Menu.Body>Menu Body</Menu.Body>
</Menu>
);
}
render(<App />, rootEl);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root" />
Another common technique used by Frameworks like Ant.Design is to map over the children and add common props to them (although context would be the better solution in my opinion)

How do I programmatically set focus on an element in a different component in React?

I have a parent component and two child components, call them child A and child B. I want to click a button in child A that will set focus on an element in child B. More specifically, I want to set focus on the first element under the <main> tag in child B.
How do I do so? Do I have to use React.createRef() in the parent and then pass that reference to both children or is there a more straightforward way to do this?
Creating a ref in the parent component like you mentioned and passing that to child B, and creating a function that will focus the ref and passing that to child A is one way of approaching it.
Example
class App extends React.Component {
ref = React.createRef();
handleClick = () => {
this.ref.current.focus();
};
render() {
return (
<div>
<ChildA onClick={this.handleClick} />
<ChildB innerRef={this.ref} />
</div>
);
}
}
function ChildA(props) {
return <button onClick={props.onClick}>Focus</button>;
}
function ChildB(props) {
return <input ref={props.innerRef} />;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

React props from parent to children

I'm a debutant with react, i read the doc. It's a general question.
I understand how to pass a props from a child component to a parent component. But, I don't understand how to pass a props from a parent component to the child component.
So in the picture, to pass a props from list to app, i do that :
in list component :
interface Props {
onCreateDoc : () => void
}
And in my app component :
I call list with the props :
<List onCreateDoc={this.onCreateDocCb}/>
And i implement the function :
onCreateDocCb = () =>{
// code of function
}
But I don't know how to pass a props from app to form. How can i do that ? Thank you
It's easier to understand how to pass props then how to receive props.
You add props to your Form Component that way:
const bar = "bar"
const fooBar = () => console.log("fooBar")
<Form stringProps="foo" varProps={bar} funcProps={fooBar}/>
Inside your Form Component you can acces it via this.props.stringProps.
Here you find examples: https://facebook.github.io/react/docs/components-and-props.html
Edit: Here is a snippet with a App and a Form Component and props passed from App to Form
// Example stateless functional component
const Form = ({ label }) => (
<div style={{color: "blue"}}>{label}</div>
);
// Example class component
class App extends React.Component {
render() {
const {title} = this.props;
return (
<div>
<div>{title}</div>
<Form label="label from props" />
</div>
);
}
}
// Render it
ReactDOM.render(
<App title="title from props" />,
document.getElementById("react")
);
<div id="react"></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>

React lambda in render [duplicate]

I'm running lint with my React app, and I receive this error:
error JSX props should not use arrow functions react/jsx-no-bind
And this is where I'm running the arrow function (inside onClick):
{this.state.photos.map(tile => (
<span key={tile.img}>
<Checkbox
defaultChecked={tile.checked}
onCheck={() => this.selectPicture(tile)}
style={{position: 'absolute', zIndex: 99, padding: 5, backgroundColor: 'rgba(255, 255, 255, 0.72)'}}
/>
<GridTile
title={tile.title}
subtitle={<span>by <b>{tile.author}</b></span>}
actionIcon={<IconButton onClick={() => this.handleDelete(tile)}><Delete color="white"/></IconButton>}
>
<img onClick={() => this.handleOpen(tile.img)} src={tile.img} style={{cursor: 'pointer'}}/>
</GridTile>
</span>
))}
Is this a bad practice that should be avoided? And what's the best way to do it?
Why you shouldn't use inline arrow functions in JSX props
Using arrow functions or binding in JSX is a bad practice that hurts performance, because the function is recreated on each render.
Whenever a function is created, the previous function is garbage collected. Rerendering many elements might create jank in animations.
Using an inline arrow function will cause PureComponents, and components that use shallowCompare in the shouldComponentUpdate method to rerender anyway. Since the arrow function prop is recreated each time, the shallow compare will identify it as a change to a prop, and the component will rerender.
As you can see in the following 2 examples - when we use inline arrow function, the <Button> component is rerendered each time (the console shows the 'render button' text).
Example 1 - PureComponent without inline handler
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
onClick = () => this.setState((prevState) => ({
counter: prevState.counter + 1
}));
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ this.onClick } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Example 2 - PureComponent with inline handler
class Button extends React.PureComponent {
render() {
const { onClick } = this.props;
console.log('render button');
return (
<button onClick={ onClick }>Click</button>
);
}
}
class Parent extends React.Component {
state = {
counter: 0
}
render() {
const { counter } = this.state;
return (
<div>
<Button onClick={ () => this.setState((prevState) => ({
counter: prevState.counter + 1
})) } />
<div>{ counter }</div>
</div>
);
}
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Binding methods to this without inlining arrow functions
Binding the method manually in the constructor:
class Button extends React.Component {
constructor(props, context) {
super(props, context);
this.cb = this.cb.bind(this);
}
cb() {
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Binding a method using the proposal-class-fields with an arrow function. As this is a stage 3 proposal, you'll need to add the Stage 3 preset or the Class properties transform to your babel configuration.
class Button extends React.Component {
cb = () => { // the class property is initialized with an arrow function that binds this to the class
}
render() {
return (
<button onClick={ this.cb }>Click</button>
);
}
}
Function Components with inner callbacks
When we create an inner function (event handler for example) inside a function component, the function will be recreated every time the component is rendered. If the function is passed as props (or via context) to a child component (Button in this case), that child will re-render as well.
Example 1 - Function Component with an inner callback:
const { memo, useState } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = () => setCounter(counter => counter + 1); // the function is recreated all the time
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
To solve this problem, we can wrap the callback with the useCallback() hook, and set the dependencies to an empty array.
Note: the useState generated function accepts an updater function, that provides the current state. In this way, we don't need to set the current state a dependency of useCallback.
Example 2 - Function Component with an inner callback wrapped with useCallback:
const { memo, useState, useCallback } = React;
const Button = memo(({ onClick }) => console.log('render button') || (
<button onClick={onClick}>Click</button>
));
const Parent = () => {
const [counter, setCounter] = useState(0);
const increment = useCallback(() => setCounter(counter => counter + 1), []);
return (
<div>
<Button onClick={increment} />
<div>{counter}</div>
</div>
);
}
ReactDOM.render(
<Parent />,
document.getElementById('root')
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>
Using inline functions like this is perfectly fine. The linting rule is outdated.
This rule is from a time when arrow functions were not as common and people used .bind(this), which used to be slow. The performance issue has been fixed in Chrome 49.
Do pay attention that you do not pass inline functions as props to a child component.
Ryan Florence, the author of React Router, has written a great piece about this:
https://reacttraining.com/blog/react-inline-functions-and-performance/
This is because an arrow function apparently will create a new instance of the function on each render if used in a JSX property. This might create a huge strain on the garbage collector and will also hinder the browser from optimizing any "hot paths" since functions will be thrown away instead of reused.
You can see the whole explanation and some more info at https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
Why shouldn't JSX props use arrow functions or bind?
Mostly, because inline functions can break memoization of optimized components:
Traditionally, performance concerns around inline functions in React have been related to how passing new callbacks on each render breaks shouldComponentUpdate optimizations in child components. (docs)
It is less about additional function creation cost:
Performance issues with Function.prototype.bind got fixed here and arrow functions are either a native thing or are transpiled by babel to plain functions; in both cases we can assume it’s not slow. (React Training)
I believe people claiming function creation is expensive have always been misinformed (React team never said this). (Tweet)
When is the react/jsx-no-bind rule useful?
You want to ensure, that memoized components work as intended:
React.memo (for function components)
PureComponent or custom shouldComponentUpdate (for class components)
By obeying to this rule, stable function object references are passed. So above components can optimize performance by preventing re-renders, when previous props have not changed.
How to solve the ESLint error?
Classes: Define the handler as method, or class property for this binding.
Hooks: Use useCallback.
Middleground
In many cases, inline functions are very convenient to use and absolutely fine in terms of performance requirements. Unfortunately, this rule cannot be limited to only memoized component types. If you still want to use it across-the-board, you could e.g. disable it for simple DOM nodes:
rules: {
"react/jsx-no-bind": [ "error", { "ignoreDOMComponents": true } ],
}
const Comp = () => <span onClick={() => console.log("Hello!")} />; // no warning
To avoid creating new functions with the same arguments, you could memoize the function bind result, here is a simple utility named memobind to do it: https://github.com/supnate/memobind
You can remove this error by wrapping the function inside useCallback.
For those wondering when you need to pass data in the callback. Ex.:
const list = todos.map((todo, index) => (
<Todo
onClick={() => { onTodoClick(todo.id, todo.title, index) }
/>
));
Solution
According to the official documentation, you should do:
Move the function arguments into the children component:
const list = todos.map((todo, index) => (
<Todo
onClick={onTodoClick}
todoId={todo.id}
todoTitle={todo.title}
indexOnList={index}
/>
));
In the children component (<Todo />), pass the arguments in the call:
function Todo(props) {
// component properties
const { onClick, todoId, todoTitle, indexOnList } = props;
// we move the call from the parent to the children
const handleClick = useCallback(() => {
onClick(todoId, todoTitle, indexOnList);
}, [todoId, todoTitle, indexOnList]);
return (
<div onClick={handleClick}>
{/* the rest of the component remains the same */}
</div>
);
}
Is this the best solution?
I dislike this solution. You end up with parent's data and logic in the children component. This makes the children component dependent on the parent component, breaking the dependency rule.
That's a big no-no for me.
What I do is just disable this rule. According to Ryan Florence (React Router author), this is not a big deal anyway:
https://medium.com/#ryanflorence/react-inline-functions-and-performance-bdff784f5578
The new (in beta, jan 2023) React tutorial uses both functions and arrow functions as JSX props. This hints strongly at this not being a major concern.
You can use arrow functions using react-cached-handler library, no need to be worried about re-rendering performance :
Note : Internally it caches your arrow functions by the specified key,
no need to be worried about re-rendering!
render() {
return (
<div>
{this.props.photos.map((photo) => (
<Photo
key={photo.url}
onClick={this.handler(photo.url, (url) => {
console.log(url);
})}
/>
))}
</div>
);
}
Other features:
Named handlers
Handle events by arrow functions
Access to the key, custom arguments and the original event
Component rendering performace
Custom context for handlers
You may also see this this error if the function you are using in your onClick handler is a regular (non-inline) function defined outside of the render method but using the function keyword.
Declare your handler function as an arrow function using const, outside of you render method, and React will stop complaining...

Resources