~/Home ~/Notes ~/Categories

React Events

25 August, 2020  ·   React
  https://www.udemy.com/course/modern-react-bootcamp/

Note: These notes are taken from Colt steele’s Modern React Bootcamp course

React Events Review

Commonly used React Events

You can attach event handlers to HTML elements in React via special reserved attributes. (You can do this in vanilla JS too, though the syntax is a bit different.)

Event Attributes

Any event you can listen for in JS, you can listen for in React.

Examples:

Example:

WiseSquare.js:

 1import React, { Component } from "react";
 2import "./WiseSquare.css";
 3
 4class WiseSquare extends Component {
 5  dispenseWisdom() {
 6    let messages = [
 7      /* wise messages go here */
 8    ];
 9    let rIndex = Math.floor(Math.random() * messages.length);
10    console.log(messages[rIndex]);
11  }
12
13  render() {
14    return (
15      <div className="WiseSquare" onMouseEnter={this.dispenseWisdom}>
16        šŸ˜ƒ
17      </div>
18    );
19  }
20}
21
22export default WiseSquare;

The Joys of Method Binding šŸ˜©

The keyword this:

When your event handlers reference the keyword this, watch out! You will lose the this context when you pass a function as a handler. Letā€™s see what happens when we try to move our quotes into defaultProps.

Example Revisited

WiseSquareWithProps.js

 1class WiseSquareWithProps extends Component {
 2  static defaultProps = {
 3    messages: [
 4      /* wise messages go here */
 5    ],
 6  };
 7
 8  dispenseWisdom() {
 9    console.log("THIS IS:", this); // undefined šŸ˜­
10    let { messages } = this.props;
11    let rIndex = Math.floor(Math.random() * messages.length);
12    console.log(messages[rIndex]);
13  }
14
15  render() {
16    return (
17      <div className="WiseSquare" onMouseEnter={this.dispenseWisdom}>
18        šŸ˜ƒ
19      </div>
20    );
21  }
22}

Fixing our binding

There are three ways to fix this:

Inline
1<div className="WiseSquare" onMouseEnter={this.dispenseWisdom.bind(this)}>
2  {/* */}
3</div>

Pros:

Cons:

Performance Issues:

Arrow Functions
1<div className="WiseSquare" onMouseEnter={() => this.dispenseWisdom()}>
2  {/* */}
3</div>

Pros:

Cons:

Performance Issues:

In the constructor
1class WiseSquareWithProps extends Component {
2  constructor(props) {
3    super(props);
4    /* do other stuff */
5    this.dispenseWisdom = this.dispenseWisdom.bind(this);
6  }
7}

Pros:

Cons:

Note : This method is being used since the introduction of ES6, previously we used to bind this using above two methods. Even though syntax looks ugly, its better to use this approach for better performance.

If calling bind annoys you, there are two ways to get around this. One is Arrow functions, which we already saw . Other method is experimental public class fields syntax, you can use class fields to correctly set bind callbacks.

Source:

 1class LoggingButton extends React.Component {
 2  // This syntax ensures `this` is bound within handleClick.
 3  // Warning: this is *experimental* syntax.
 4
 5  // babel will bind this in a constructor
 6
 7/*BEHIND THE SCENES ...*/
 8
 9  // constructor(){
10  //   this.handleClick = () =>{
11  //     /**/
12  //   }
13
14  }
15  handleClick = () => {
16    console.log("this is:", this);
17  };
18
19  render() {
20    return <button onClick={this.handleClick}>Click me</button>;
21  }
22}

Method Binding with Arguments

In our previous examples, this.dispenseWisdom didnā€™t take any arguments. But what if we need to pass arguments to an event handler?

Example:

ButtonList.js

 1class ButtonList extends Component {
 2  static defaultProps = {
 3    colors: ["green", "red", "blue", "peachpuff"],
 4  };
 5
 6  handleClick(color) {
 7    console.log(`You clicked on the ${color} button.`);
 8  }
 9
10  render() {
11    return (
12      <div className="ButtonList">
13        {this.props.colors.map((c) => {
14          const colorObj = { backgroundColor: c };
15          return (
16            <button style={colorObj} onClick={this.handleClick.bind(this, c)}>
17              Click on me!
18            </button>
19          );
20        })}
21      </div>
22    );
23  }
24}

Inside of a loop, you can bind and pass in additional arguments. Also possible to use an arrow function, both these approaches suffer from the same performance downsides weā€™ve already seen. We can do better, but first we need to talk aboutā€¦

Passing functions to child components

How data flows

What it looks like?

Not an ideal way:

NumberList.js :

 1class NumberList extends Component {
 2  constructor(props) {
 3    super(props);
 4    this.state = { nums: [1, 2, 3, 4, 5] };
 5  }
 6
 7  remove(num) {
 8    this.setState((st) => ({
 9      nums: st.nums.filter((n) => n !== num),
10    }));
11  }
12
13  render() {
14    let nums = this.state.nums.map((n) => (
15      <NumberItem value={n} remove={() => this.remove(n)} />
16    ));
17    return <ul>{nums}</ul>;
18  }
19}

NumberItem.js :

 1class NumberItem extends Component {
 2  render() {
 3    return (
 4      <li>
 5        {this.props.value}
 6        <button onClick={this.props.remove}>X</button>
 7      </li>
 8    );
 9  }
10}

Using a single bound function

BetterNumList.js

 1class BetterNumList extends Component {
 2  constructor(props) {
 3    super(props);
 4    this.state = { nums: [1, 2, 3, 4, 5] };
 5    this.remove = this.remove.bind(this);
 6  }
 7
 8  remove(num) {
 9    this.setState((st) => ({
10      nums: st.nums.filter((n) => n !== num),
11    }));
12  }
13
14  render() {
15    let nums = this.state.nums.map((n) => (
16      <BetterNumItem value={n} remove={this.remove} />
17    ));
18    return <ul>{nums}</ul>;
19  }
20}

BetterNumItem.js

 1class NumberItem extends Component {
 2  constructor(props) {
 3    super(props);
 4    this.handleRemove = this.handleRemove.bind(this);
 5  }
 6
 7  handleRemove() {
 8    this.props.remove(this.props.value);
 9  }
10
11  render() {
12    return (
13      <li>
14        {this.props.value}
15        <button onClick={this.handleRemove}>X</button>
16      </li>
17    );
18  }
19}

Where to bind?

Naming Conventions


Lists and Keys

BetterNumList.js :

1class BetterNumList extends Component {
2  render() {
3    let nums = this.state.nums.map((n) => (
4      <BetterNumItem value={n} remove={this.remove} />
5    ));
6    return <ul>{nums}</ul>;
7  }
8}

When mapping over data and returning components, you get a warning about keys for list items.key is a special string attr to include when creating lists of elements

When choosing a keyit is important to note that, it need to be unique.

Adding keys

Letā€™s assign a key to our list items inside nums.map():

1class NumberList extends Component {
2  render() {
3    const nums = this.state.nums.map((n) => (
4      <NumberItem value={n} key={n} remove={this.remove} />
5    ));
6    return <ul>{nums}</ul>;
7  }
8}

Keys

Keys help React identify which items are changed/added/removed. Keys should be given to repeated elems to provide a stable identity.

Picking a key

Best way: use string that uniquely identifies item among siblings. Most often you would use IDs from your data [data from db or api etc.] as keys:

1let todoItems = this.state.todos.map((todo) => (
2  <li key={todo.id}>{todo.text}</li>
3));

Last resort

When you donā€™t have stable IDs for rendered items, you may use the iteration index as a key as a last resort

1// Only do this if items have no stable IDs
2
3const todoItems = this.state.todos.map((todo, index) => (
4  <li key={index}>{todo.text}</li>
5));

A good rule of thumb:

Donā€™t use indexes for keys if item order may change or items can be deleted. This can cause performance problems or bugs with component state.

A goodread: Index as a key is an anti-pattern


shortid &uuid npm packages helps us to create unique ids

 react  javascript  es6
React State Patterns↠ ↞React Forms