Brief Notes on Learning React

发表于 2022-02-05 14:30 976 字 5 min read

cos avatar

cos

FE / ACG / 手工 / 深色模式强迫症 / INFP / 兴趣广泛养两只猫的老宅女 / remote

文章介绍了 React 中事件处理、列表 key 的使用、表单(特别是受控组件)以及状态提升的核心概念。强调事件处理应使用函数而非字符串,通过 preventDefault 阻止默认行为;key 用于标识列表元素,确保在兄弟节点间唯一;表单通过受控组件将输入值与组件状态绑定,实现数据流的统一管理;通过状态提升,将共享状态集中到父组件中,提升代码可维护性和可调试性。

This article has been machine-translated from Chinese. The translation may contain inaccuracies or awkward phrasing. If in doubt, please refer to the original Chinese version.

Event Handling

Handling Events – React (docschina.org)

  • You need to pass a function as the event handler, rather than a string.
<button onClick={activateLasers}>
  Activate Lasers
</button>
  • You cannot prevent default behavior by returning false. You must explicitly call preventDefault.
function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('The link was clicked.');
  }

  return (
    <a href="#" onClick={handleClick}>
      Click me
    </a>
  );
}

When using React, you generally don’t need to use addEventListener to add listeners to already-created DOM elements. In fact, you only need to add listeners when the element is initially rendered. Declare event handling functions as methods in the class.

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
    // This binding is essential to make `this` work in the callback
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
      this.setState(state => ({
          isToggleOn: !state.isToggleOn
      }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
            {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

Lists and Keys

Lists & Keys – React (docschina.org)

Keys

Keys help React identify which elements have changed, such as being added or removed. Therefore, you should give each element in an array a definitive identifier.

The best key for an element is a unique string that the element has within the list. Typically, we use the id from the data as the element’s key.

Keys Must Be Unique Among Siblings

Keys used among array elements should be unique among their sibling nodes. However, they don’t need to be globally unique. When we generate two different arrays, we can use the same key values.

Forms

Forms – React (docschina.org)

In React, HTML form elements work somewhat differently from other DOM elements, because form elements typically maintain some internal state.

Controlled Components

In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the component’s state property and can only be updated using setState().

We can combine these two approaches to make React’s state the “single source of truth”. The React component that renders the form also controls what happens in the form during user input. A form input element whose value is controlled by React in this way is called a “controlled component.”

The textarea Tag

In HTML, the <textarea> element defines its text through its children:

<textarea>
  Hello, this is text inside a text area
</textarea>

In React, <textarea> uses a value attribute instead. This makes forms using <textarea> very similar to forms using single-line inputs:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {      value: 'Please write an essay about your favorite DOM element.'    };
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {    this.setState({value: event.target.value});  }
  handleSubmit(event) {
    alert('Submitted essay: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

Note that this.state.value is initialized in the constructor, so the text area has a default initial value.

Lifting State Up

Often, several components need to reflect the same changing data. We recommend lifting the shared state up to the closest common ancestor component. Let’s see how this works.

Lifting State Up – React (docschina.org)

We’ll start with a component called BoilingVerdict. It accepts a celsius temperature as a prop and prints whether it is enough to boil water.

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;  }
  return <p>The water would not boil.</p>;}

Next, we’ll create a component called Calculator. It renders an <input> for entering the temperature and keeps its value in this.state.temperature.

Additionally, it renders the BoilingVerdict component based on the current input value.

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};  }

  handleChange(e) {
    this.setState({temperature: e.target.value});  }

  render() {
    const temperature = this.state.temperature;    return (
      <fieldset>
        <legend>Enter temperature in Celsius:</legend>
        <input          value={temperature}          onChange={this.handleChange} />        <BoilingVerdict          celsius={parseFloat(temperature)} />      </fieldset>
    );
  }
}

Adding a Second Input

Our new requirement is to provide a Fahrenheit input in addition to the existing Celsius input, and keep the two inputs synchronized.

We’ll extract a TemperatureInput component from Calculator, and add a new scale prop to it that can be either "c" or "f":

const scaleNames = {  c: 'Celsius',  f: 'Fahrenheit'};
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

We can now modify the Calculator component to render two independent temperature input components:

We’ll extract a TemperatureInput component from Calculator, and add a new scale prop to it that can be either "c" or "f":

In a React application, any mutable data should have a single corresponding “source of truth.” Usually, state is first added to the component that needs it for rendering. Then, if other components also need this state, you can lift it up to the closest common ancestor. You should rely on the top-down data flow, rather than trying to synchronize state between different components.

While lifting state requires writing more “boilerplate” code than two-way binding, the benefit is that finding and isolating bugs takes less work. Since any state “lives” in a component and only that component can change it, the surface area for bugs is greatly reduced. Additionally, you can implement custom logic to reject or transform user input.

喜欢的话,留下你的评论吧~

© 2020 - 2026 cos @cosine
Powered by theme astro-koharu · Inspired by Shoka