8种方法帮助你写出高效的 React 组件

举报
Ocean2022 发表于 2022/04/19 15:55:01 2022/04/19
【摘要】 ES6中的JavaScript已添加了许多功能。 这些更改有助于开发人员编写简短,易于理解和维护的代码。当您使用create-react-app创建React App时,您已经支持这些更改。 这是因为它使用Babel.js将ES6 +代码转换为所有浏览器都能理解的ES5代码。

ES6中的JavaScript已添加了许多功能。 这些更改有助于开发人员编写简短,易于理解和维护的代码。当您使用create-react-app创建React App时,您已经支持这些更改。 这是因为它使用Babel.js将ES6 +代码转换为所有浏览器都能理解的ES5代码。

在本文中,我们将探索各种方法来编写更短,更简单和更易于理解的React代码。

看看下面的代码:

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      number1: "",
      number2: "",
      result: "",
      errorMsg: ""
    };
    this.onFirstInputChange = this.onFirstInputChange.bind(this);
    this.onSecondInputChange = this.onSecondInputChange.bind(this);
    this.handleAdd = this.handleAdd.bind(this);
    this.handleSubtract = this.handleSubtract.bind(this);
  }

  onFirstInputChange(event) {
    const value = event.target.value;
    this.setState({
      number1: value
    });
  }

  onSecondInputChange(event) {
    const value = event.target.value;
    this.setState({
      number2: value
    });
  }

  handleAdd(event) {
    event.preventDefault();
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    const sum = number1 + number2;
    if (isNaN(sum)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: sum
      });
    }
  }

  handleSubtract(event) {
    event.preventDefault();
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    const subtraction = number1 - number2;
    if (isNaN(subtraction)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: subtraction
      });
    }
  }

  render() {
    return (
      <div>
        <div className="input-section">
          {this.state.errorMsg && (
            <p className="error-msg">{this.state.errorMsg}</p>
          )}
          <label>First Number</label>
          <input
            type="text"
            name="number1"
            placeholder="Enter a number"
            value={this.state.number1}
            onChange={this.onFirstInputChange}
          />
        </div>
        <div className="input-section">
          <label>Second Number</label>
          <input
            type="text"
            name="number2"
            placeholder="Enter a number"
            value={this.state.number2}
            onChange={this.onSecondInputChange}
          />
        </div>
        <div className="result-section">
          Result: <span className="result">{this.state.result}</span>
        </div>
        <button type="button" className="btn" onClick={this.handleAdd}>
          Add
        </button>
        <button type="button" className="btn" onClick={this.handleSubtract}>
          Subtract
        </button>
      </div>
    );
  }
}

在这里插入图片描述

在这里,我们有两个输入文本框,用于接收用户的输入,还有两个按钮,用于计算作为输入提供的数字的加法和减法。

1.避免手动绑定事件处理程序

如您在React中所知,当我们附加任何onClick或onChange或任何其他事件处理程序时,如下所示:

<input
  ...
  onChange={this.onFirstInputChange}
/>

然后,处理函数(onFirstInputChange)不会保留此绑定。

这不是React的问题,而是JavaScript事件处理程序的工作方式。

因此,我们必须使用.bind方法像这样正确地绑定它:

constructor(props) {
  this.onFirstInputChange = this.onFirstInputChange.bind(this);
  this.onSecondInputChange = this.onSecondInputChange.bind(this);
  this.handleAdd = this.handleAdd.bind(this);
  this.handleSubtract = this.handleSubtract.bind(this);
}

上面的代码行将在处理程序函数中正确维护此类的绑定。

但是,为每个新的事件处理程序添加新的绑定代码很繁琐。 幸运的是,我们可以使用类属性语法对其进行修复。

使用类属性使我们可以直接在类内部定义属性。

对于Babel版本> = 7,Create-react-app内部使用@ babel / babel-plugin-transform-class-properties插件,而对于Babel版本<7,则使用babel / plugin-proposal-class-properties插件,因此您不必 手动配置它。

要使用它,我们需要将事件处理函数转换为箭头函数语法。


onFirstInputChange(event) {
  const value = event.target.value;
  this.setState({
    number1: value
  });
}

上面的代码可以如下编写:(箭头函数改写)

onFirstInputChange = (event) => {
  const value = event.target.value;
  this.setState({
    number1: value
  });
}

以类似的方式,我们可以转换其他三个函数:

onSecondInputChange = (event) => {
 // your code
}

handleAdd = (event) => {
 // your code
}

handleSubtract = (event) => {
 // your code
}

同样,也不需要在构造函数中绑定事件处理程序。 因此,我们可以删除该代码。 现在,构造函数将如下所示:

constructor(props) {
  super(props);

  this.state = {
    number1: "",
    number2: "",
    result: "",
    errorMsg: ""
  };
}

我们可以进一步简化它。 类属性语法允许我们直接在类内部声明任何变量,因此我们可以完全删除构造函数并将状态声明为类的一部分,如下所示:

export default class App extends React.Component {
  state = {
    number1: "",
    number2: "",
    result: "",
    errorMsg: ""
  };

  render() { }
}

整体改写后:

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  state = {
    number1: "",
    number2: "",
    result: "",
    errorMsg: ""
  };

  onFirstInputChange = (event) => {
    const value = event.target.value;
    this.setState({
      number1: value
    });
  };

  onSecondInputChange = (event) => {
    const value = event.target.value;
    this.setState({
      number2: value
    });
  };

  handleAdd = (event) => {
    event.preventDefault();
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    const sum = number1 + number2;
    if (isNaN(sum)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: sum
      });
    }
  };

  handleSubtract = (event) => {
    event.preventDefault();
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    const subtraction = number1 - number2;
    if (isNaN(subtraction)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: subtraction
      });
    }
  };

  render() {
    return (
      <div>
        <div className="input-section">
          {this.state.errorMsg && (
            <p className="error-msg">{this.state.errorMsg}</p>
          )}
          <label>First Number</label>
          <input
            type="text"
            name="number1"
            placeholder="Enter a number"
            value={this.state.number1}
            onChange={this.onFirstInputChange}
          />
        </div>
        <div className="input-section">
          <label>Second Number</label>
          <input
            type="text"
            name="number2"
            placeholder="Enter a number"
            value={this.state.number2}
            onChange={this.onSecondInputChange}
          />
        </div>
        <div className="result-section">
          Result: <span className="result">{this.state.result}</span>
        </div>
        <button type="button" className="btn" onClick={this.handleAdd}>
          Add
        </button>
        <button type="button" className="btn" onClick={this.handleSubtract}>
          Subtract
        </button>
      </div>
    );
  }
}

2.使用单个事件处理程序方法

如果检查上面的代码,您将看到,对于每个输入字段,我们都有一个单独的事件处理程序函数onFirstInputChange和onSecondInputChange。

如果输入字段的数量增加,那么事件处理程序函数的数量也会增加,这是不好的。

例如,如果您要创建注册页面,那么会有很多输入字段。 因此,为每个输入字段创建单独的处理程序函数是不可行的。

让我们改变它。

要创建将处理所有输入字段的单个事件处理程序,我们需要为每个输入字段指定一个唯一名称,该名称与相应的状态变量名称完全匹配。

我们已经有了这个设置。 我们在状态中还定义了我们为输入字段指定的名称number1和number2。 因此,我们将两个输入字段的处理程序方法更改为onInputChange,如下所示:

<input
  type="text"
  name="number1"
  placeholder="Enter a number"
  onChange={this.onInputChange}
/>

<input
  type="text"
  name="number2"
  placeholder="Enter a number"
  onChange={this.onInputChange}
/>

并添加一个新的onInputChange事件处理程序,如下所示:

onInputChange = (event) => {
  const name = event.target.name;
  const value = event.target.value;
  this.setState({
    [name]: value
  });
};

在这里,设置状态时,我们使用动态值设置动态状态名称。 因此,当我们更改number1输入字段的值时,event.target.name将为number1,event.target.value将为用户输入的值。

当我们更改number2输入字段的值时,event.target.name将为number2,event.taget.value将为用户输入的值。

因此,这里我们使用ES6动态键语法来更新状态的相应值。

现在,您可以删除onFirstInputChange和onSecondInputChange事件处理程序方法。 我们不再需要它们。

完成代码如下:

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  state = {
    number1: "",
    number2: "",
    result: "",
    errorMsg: ""
  };

  onInputChange = (event) => {
    const name = event.target.name;
    const value = event.target.value;
    this.setState({
      [name]: value
    });
  };

  handleAdd = (event) => {
    event.preventDefault();
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    const sum = number1 + number2;
    if (isNaN(sum)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: sum
      });
    }
  };

  handleSubtract = (event) => {
    event.preventDefault();
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    const subtraction = number1 - number2;
    if (isNaN(subtraction)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: subtraction
      });
    }
  };

  render() {
    return (
      <div>
        <div className="input-section">
          {this.state.errorMsg && (
            <p className="error-msg">{this.state.errorMsg}</p>
          )}
          <label>First Number</label>
          <input
            type="text"
            name="number1"
            placeholder="Enter a number"
            value={this.state.number1}
            onChange={this.onInputChange}
          />
        </div>
        <div className="input-section">
          <label>Second Number</label>
          <input
            type="text"
            name="number2"
            placeholder="Enter a number"
            value={this.state.number2}
            onChange={this.onInputChange}
          />
        </div>
        <div className="result-section">
          Result: <span className="result">{this.state.result}</span>
        </div>
        <button type="button" className="btn" onClick={this.handleAdd}>
          Add
        </button>
        <button type="button" className="btn" onClick={this.handleSubtract}>
          Subtract
        </button>
      </div>
    );
  }
}

3. 使用单一计算方法

现在,让我们重构handleAdd和handleSubtract方法。

我们使用两种几乎具有相同代码的独立方法来创建代码重复。 我们可以通过创建单个方法并将参数传递给标识加法或减法运算的函数来解决此问题。

// change the below code:
<button type="button" className="btn" onClick={this.handleAdd}>
  Add
</button>

<button type="button" className="btn" onClick={this.handleSubtract}>
  Subtract
</button>

// to this code:
<button type="button" className="btn" onClick={() => this.handleOperation('add')}>
  Add
</button>

<button type="button" className="btn" onClick={() => this.handleOperation('subtract')}>
  Subtract
</button>

在这里,我们为onClick处理程序添加了一个新的内联方法,其中我们通过传递操作名称来手动调用新的handleOperation方法。

现在,添加一个新的handleOperation方法,并删除以前添加的handleAdd和handleSubtract方法。如下所示:

handleOperation = (operation) => {
  const number1 = parseInt(this.state.number1, 10);
  const number2 = parseInt(this.state.number2, 10);

  let result;
  if (operation === "add") {
    result = number1 + number2;
  } else if (operation === "subtract") {
    result = number1 - number2;
  }

  if (isNaN(result)) {
    this.setState({
      errorMsg: "Please enter valid numbers."
    });
  } else {
    this.setState({
      errorMsg: "",
      result: result
    });
  }
};

完整代码如下:

import React from "react";
import "./styles.css";

export default class App extends React.Component {
  state = {
    number1: "",
    number2: "",
    result: "",
    errorMsg: ""
  };

  onInputChange = (event) => {
    const name = event.target.name;
    const value = event.target.value;
    this.setState({
      [name]: value
    });
  };

  handleOperation = (operation) => {
    const number1 = parseInt(this.state.number1, 10);
    const number2 = parseInt(this.state.number2, 10);

    let result;
    if (operation === "add") {
      result = number1 + number2;
    } else if (operation === "subtract") {
      result = number1 - number2;
    }

    if (isNaN(result)) {
      this.setState({
        errorMsg: "Please enter valid numbers."
      });
    } else {
      this.setState({
        errorMsg: "",
        result: result
      });
    }
  };

  render() {
    return (
      <div>
        <div className="input-section">
          {this.state.errorMsg && (
            <p className="error-msg">{this.state.errorMsg}</p>
          )}
          <label>First Number</label>
          <input
            type="text"
            name="number1"
            placeholder="Enter a number"
            value={this.state.number1}
            onChange={this.onInputChange}
          />
        </div>
        <div className="input-section">
          <label>Second Number</label>
          <input
            type="text"
            name="number2"
            placeholder="Enter a number"
            value={this.state.number2}
            onChange={this.onInputChange}
          />
        </div>
        <div className="result-section">
          Result: <span className="result">{this.state.result}</span>
        </div>
        <button
          type="button"
          className="btn"
          onClick={() => this.handleOperation("add")}
        >
          Add
        </button>
        <button
          type="button"
          className="btn"
          onClick={() => this.handleOperation("subtract")}
        >
          Subtract
        </button>
      </div>
    );
  }
}

4.使用ES6解构语法

在onInputChange方法中,我们有如下代码:

const name = event.target.name;
const value = event.target.value;

我们可以使用ES6解构语法来简化它,如下所示:

const { name, value } = event.target;

在这里,我们从对象中提取name和value属性,event.target并创建局部name和value变量来存储这些值。

现在,在handleOperation方法内部,我们不必每次使用this.state.number1和时都引用状态 this.state.number2,而是可以预先分离出这些变量:

// change the below code:

const number1 = parseInt(this.state.number1, 10);
const number2 = parseInt(this.state.number2, 10);

// to this code:

let { number1, number2 } = this.state;
number1 = parseInt(number1, 10);
number2 = parseInt(number2, 10);

5.使用增强的对象字面量语法

如果您检查setState函数内部的函数调用handleOperation,则如下所示:

this.setState({
  errorMsg: "",
  result: result
});

我们可以使用增强的对象字面量语法来简化此代码。

如果属性名与变量名完全匹配result: result那么我们可以跳过在冒号后面提到的部分。因此,上面的setState函数调用可以这样简化:

this.setState({
  errorMsg: "",
  result
});

6.将类组件转换为React Hooks

从React版本16.8.0开始,React添加了一种使用React Hooks在函数组件内部使用状态和生命周期方法的方法

**使用React Hooks可以使我们编写的代码短得多,并且易于维护和理解。**因此,让我们将上面的代码转换为使用React Hooks语法。

如果您不熟悉React Hooks,请查看React Hooks的介绍。

首先让我们将App组件声明为函数组件:

const App = () => {

};

export default App;

要声明状态,我们需要使用useState钩子,因此将其导入文件顶部。然后创建3个useState,一个用于将数字存储在一起作为对象。我们可以使用一个处理函数和两个其他useState调用来一起更新它们,以存储结果和错误消息。

import React, { useState } from "react";

const App = () => {
  const [state, setState] = useState({
    number1: "",
    number2: ""
  });
  const [result, setResult] = useState("");
  const [errorMsg, setErrorMsg] = useState("");
};

export default App;

将onInputChange处理程序方法更改为此:

const onInputChange = () => {
  const { name, value } = event.target;

  setState((prevState) => {
    return {
      ...prevState,
      [name]: value
    };
  });
};

在这里,我们使用updater语法设置状态,因为在使用React Hooks时,更新对象时状态不会自动合并

因此,我们首先分散状态的所有属性,然后添加新的状态值

将handleOperation方法更改为此:

const handleOperation = (operation) => {
  let { number1, number2 } = state;
  number1 = parseInt(number1, 10);
  number2 = parseInt(number2, 10);

  let result;
  if (operation === "add") {
    result = number1 + number2;
  } else if (operation === "subtract") {
    result = number1 - number2;
  }

  if (isNaN(result)) {
    setErrorMsg("Please enter valid numbers.");
  } else {
    setErrorMsg("");
    setResult(result);
  }
};

完整代码:

import React, { useState } from "react";
import "./styles.css";

const App = () => {
  const [state, setState] = useState({
    number1: "",
    number2: ""
  });
  const [result, setResult] = useState("");
  const [errorMsg, setErrorMsg] = useState("");

  const onInputChange = (event) => {
    const { name, value } = event.target;

    setState((prevState) => {
      return {
        ...prevState,
        [name]: value
      };
    });
  };

  const handleOperation = (operation) => {
    let { number1, number2 } = state;
    number1 = parseInt(number1, 10);
    number2 = parseInt(number2, 10);

    let result;
    if (operation === "add") {
      result = number1 + number2;
    } else if (operation === "subtract") {
      result = number1 - number2;
    }

    if (isNaN(result)) {
      setErrorMsg("Please enter valid numbers.");
    } else {
      setErrorMsg("");
      setResult(result);
    }
  };

  return (
    <div>
      <div className="input-section">
        {errorMsg && <p className="error-msg">{errorMsg}</p>}
        <label>First Number</label>
        <input
          type="text"
          name="number1"
          placeholder="Enter a number"
          value={state.number1}
          onChange={onInputChange}
        />
      </div>
      <div className="input-section">
        <label>Second Number</label>
        <input
          type="text"
          name="number2"
          placeholder="Enter a number"
          value={state.number2}
          onChange={onInputChange}
        />
      </div>
      <div className="result-section">
        Result: <span className="result">{result}</span>
      </div>
      <button
        type="button"
        className="btn"
        onClick={() => handleOperation("add")}
      >
        Add
      </button>
      <button
        type="button"
        className="btn"
        onClick={() => handleOperation("subtract")}
      >
        Subtract
      </button>
    </div>
  );
};

export default App;

7.隐式返回对象

现在,我们已经优化了代码以使用现代ES6功能并避免了代码重复。我们可以做的另一件事就是简化setState函数调用。

如果检查处理程序中的当前setState函数调用onInputChange,则如下所示:

setState((prevState) => {
  return {
    ...prevState,
    [name]: value
  };
});

在箭头函数中,如果我们有这样的代码:

const add = (a, b) => {
 return a + b;
}

然后我们可以简化它,如下所示:

const add = (a, b) => a + b;

之所以行之有效,是因为如果箭头函数主体中只有一条语句,那么我们可以跳过花括号和return关键字。这称为隐式收益

因此,如果我们要从箭头函数返回这样的对象:

const getUser = () => {
 return {
  name: 'David,
  age: 35
 }
}

我们就不能像这样简化它

const getUser = () => {
  name: 'David,
  age: 35
}

因为大括号表示函数的开始,因此上述代码无效。为了使其工作,我们可以将对象包装在圆括号中,如下所示:

const getUser = () => ({
  name: 'David,
  age: 35
})

上面的代码与下面的代码等效:

const getUser = () => {
 return {
  name: 'David,
  age: 35
 }
}

因此,我们可以使用相同的技术来简化setState函数调用。

setState((prevState) => {
  return {
    ...prevState,
    [name]: value
  };
});

// the above code can be simplified as:

setState((prevState) => ({
  ...prevState,
  [name]: value
}));

在React中经常使用这种将代码包装在方括号中的技术:

  • 定义功能组件:
const User = () => (
   <div>
    <h1>Welcome, User</h1>
    <p>You're logged in successfully.</p>
   </div>
);
  • 在react-redux中的mapStateToProps函数内部:
const mapStateToProps = (state, props) => ({ 
   users: state.users,
   details: state.details
});
  • Redux action creator:
const addUser = (user) => ({
  type: 'ADD_USER',
  user
});

8.编写更好的React组件的额外技巧

如果我们有这样的组件:

const User = (props) => (
   <div>
    <h1>Welcome, User</h1>
    <p>You're logged in successfully.</p>
   </div>
);

然后希望将它仅进行测试或调试,而不是将代码转换为以下代码:

const User = (props) => {
 console.log(props);
 return (
   <div>
    <h1>Welcome, User</h1>
    <p>You're logged in successfully.</p>
   </div>
 );
}

可以 || 像这样使用逻辑OR运算符():

const User = (props) => console.log(props) || (
   <div>
    <h1>Welcome, User</h1>
    <p>You're logged in successfully.</p>
   </div>
);

该console.log函数仅打印传递给它的值,但不返回任何内容–因此它将被评估为未定义||(…)。

并且由于||运算符返回第一个真实值,因此之后的代码||也将被执行

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。