React中文文档阅读总结——快速入门

443 阅读11分钟

快速入门

点击进入——React中文文档


JSX及元素渲染

  1. React 是一个 JavaScript 库

  2. const element = <h1>Hello, world!</h1>; 考虑一下这个变量的声明,它既不是字符串也不是HTML,这就是 JSX ,它是 JavaScrip 的一种扩展语法。我们推荐在 React 中使用这种语法来描述 UI 信息,它具有 JavaScrip 的全部能力,可以生成React “元素”。

  3. 比起 HTML,JSX 更接近于 JavaScript,所以 React DOM 使用驼峰(camelCase)属性命名约定,而不是HTML属性名称。 例如,class 在JSX中变为classNametabindex 变为 tabIndex

  4. 元素(Elements)是 React 应用中最小的构建部件,它是构成组建的“材料”,一个元素用于描述你在将在屏幕上看到的内容,例如:const element = <h1>Hello, world</h1>;

  5. React 元素是 不可突变(immutable) 的,一旦你创建了一个元素, 就不能再修改其子元素或任何属性。一个元素就像电影里的一帧: 它表示在某一特定时间点的 UI 。更新 UI 的唯一方法是创建一个新的元素, 并将其传入ReactDOM.render() 方法。但实际上,大多数 React 应用只会调用 ReactDOM.render() 一次。更新UI更多的是通过与 状态(state)生命周期 挂钩来实现的。

组件(Components) 和 属性(Props)

  1. 组件名称总是以大写字母开始

  2. 组件必须返回一个单独的根元素。

  3. function sum(a, b) { return a + b; },这种函数称为 “ 纯函数 ” ,因为它们不会试图改变它们的输入,并且 对于同样的输入,始终可以得到相同的结果 。 反之, 以下是非纯函数, 因为它改变了自身的输入值: function withdraw(account, amount) { account.total -= amount; }

  4. 所有 React 组件都必须是 纯函数,并禁止修改其自身 props 。

状态(State) 和 生命周期

  1. this.props 由 React 本身设定, 而 this.state 具有特殊的含义,但如果需要存储一些不用于视觉输出的内容,则可以手动向类中添加额外的字段。 如果在 render() 方法中没有被引用, 它不应该出现在 state

  2. 关于 setState() 有三件事是你应该知道的:

  • 不要直接修改 state(状态)

    例如,这样将不会重新渲染一个组件:this.state.comment = 'Hello';

    正确的做法是用 setState() 来修改 statethis.setState({comment: 'Hello'});

  • state(状态) 更新可能是异步的

    React 为了优化性能,有可能会将多个 setState() 调用合并为一次更新。因为 this.propsthis.state 可能是异步更新的,你不能依赖他们的值计算下一个state(状态)。

    例如, 以下代码可能导致 counter(计数器)更新失败:

    // 错误
    this.setState({
      counter: this.state.counter + this.props.increment,
    });
    

    要弥补这个问题,使用另一种 setState() 的形式,它 接受一个函数而不是一个对象 。这个函数将接收 前一个状态 作为 第一个参数 ,应用 更新时的 props 作为 第二个参数

    // 正确
    this.setState((prevState, props) => ({
      counter: prevState.counter + props.increment
    }));
    
  • state(状态)更新会被合并

    当你调用 setState() , React 将合并你提供的对象到当前的状态中。

  • 数据向下流动

    如果把组件树想像为 props(属性) 的瀑布,所有组件的 state(状态) 就如同一个额外的水源汇入主流,且只能随着主流的方向向下流动。

    任何 state(状态) 始终由某个特定组件所有,并且从该 state(状态) 导出的任何数据 或 UI 只能影响树中 “下方” 的组件。

处理事件

  1. 通过 React 元素处理事件跟在 DOM 元素上处理事件非常相似。但是有一些语法上的区别:
  • React 事件使用驼峰命名,而不是全部小写。

  • 通过 JSX , 你传递一个函数作为事件处理程序,而不是一个字符串。

  • 在 React 中你不能通过返回 false 来阻止默认行为。必须明确调用 preventDefault 。例如,对于纯 HTML ,要阻止链接打开一个新页面的默认行为,可以这样写:

    <a href="#" onclick="console.log('The link was clicked.'); return false">
      Click me
    </a>
    

    在 React 中, 应该这么写:

    function ActionLink() {
      function handleClick(e) {
        e.preventDefault();
        console.log('The link was clicked.');
      }
    
      return (
        <a href="#" onClick={handleClick}>
          Click me
        </a>
      );
    }
    

条件渲染

  1. 在 React 中,你可以创建不同的组件封装你所需要的行为。然后,只渲染它们之中的一些,取决于你的应用的状态。 React 中的条件渲染就和在 JavaScript 中的条件语句一样 。使用 JavaScript 操作符如 if 或者条件操作符来创建渲染当前状态的元素,并且让 React 更新匹配的 UI 。

    虽然声明一个变量并使用一个 if 语句是一个有条件地渲染组件的好方法,有时你可能想要使用一个更简短的语法。在 JSX 中有几种内联条件的方法,如下所述。

    • 使用逻辑 && 操作符的内联 if 用法

      例如当数组arr不为空时渲染组件 <ShowMessages> ,可以写成

      { 
          arr.length>0 &&
          <ShowMessages>
      }
      

      它可以正常运行,因为在 JavaScript 中, true && expression 总是会评估为 expression ,而 false && expression 总是执行为 false 。 因此,如果条件为 true ,则 && 后面的元素将显示在输出中。 如果是 false,React 将会忽略并跳过它

    • 使用条件操作符的内联 If-Else

      另一个用于条件渲染元素的内联方法是使用 JavaScript 的条件操作符 If-Else 或者是三目运算符 condition ? true : false

    还要记住,无论何时何地,当条件变得太复杂时,可能是提取组件的好时机

  2. 防止组件渲染

    在极少数情况下,您可能希望组件隐藏自身,即使它是由另一个组件渲染的。为此,返回 null 而不使其渲染输出

    例如在如下代码中:

    //方法一:
    function WarningBanner(props) {
      if (!props.warn) {
        return null;
      }
      return (
        <div className="warning">
          Warning!
        </div>
      );
    }
    
    class Page extends React.Component {
      constructor(props) {
        super(props);
        this.state = {showWarning: true}
        this.handleToggleClick = this.handleToggleClick.bind(this);
      }
    
      handleToggleClick() {
        this.setState(prevState => ({
          showWarning: !prevState.showWarning
        }));
      }
      
      render() {
        return (
          <div>
            <WarningBanner warn={this.state.showWarning} />
            <button onClick={this.handleToggleClick}>
              {this.state.showWarning ? 'Hide' : 'Show'}
            </button>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Page />,
      document.getElementById('root')
    );
    

    Hide按钮可以控制 Warning div 的显示隐藏。 同样,以下代码也能达到相同效果:

    //方法二:
    class Page extends React.Component {
      constructor(props) {
        super(props);
        this.state = {showWarning: true}
        this.handleToggleClick = this.handleToggleClick.bind(this);
      }
    
      handleToggleClick() {
        this.setState(prevState => ({
          showWarning: !prevState.showWarning
        }));
      }
      
      render() {
        return (
          <div>
            <div className="warning" style={{display:this.state.showWarning?'':'none'}}>
              Warning!
            </div>
            <button onClick={this.handleToggleClick}>
              {this.state.showWarning ? 'Hide' : 'Show'}
            </button>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Page />,
      document.getElementById('root')
    );
    

    这里没有把 Warning div 当做组件抽离出来,虽然也能达到相同的效果,但是使用 display 只是控制了 Warning div 的显示隐藏,不论如何,Warning div 其实都已经在页面加载了,消耗了资源;而第一种方法返回 null ,却是完完全全的控制了 Warning div 的加载,性能上更胜一筹。

    PS:从组件的 render 方法返回 null 不会影响组件生命周期方法的触发。 例如, componentWillUpdatecomponentDidUpdate 仍将被调用。

列表(Lists) 和 键(Keys)

  1. 首先,先来看一段代码:

    const numbers = [1, 2, 3, 4, 5];
    const listItems = numbers.map((numbers) =>
      <li>{numbers}</li>
    );
    
    ReactDOM.render(
      <ul>{listItems}</ul>,
      document.getElementById('root')
    );
    

    这段代码显示从 1 到 5 的数字列表。

    当运行上述代码时,虽然会正确显示出列表,但是会收到一个警告:a key should be provided for list items(应该为列表元素提供一个键)。当创建元素列表时,“key” 是一个你需要包含的特殊字符串属性。 键(Keys) 帮助 React 标识哪个项被修改、添加或者移除了 。数组中的 每一个元素 都应该有一个 唯一不变 的键(Keys)来标识。挑选 key 最好的方式是使用一个在它的同辈元素中不重复的标识字符串。多数情况你可以使用数据中的 ID 作为 keys;当要渲染的列表项中没有稳定的 IDs 时,你可以使用数据项的索引值作为 key 的最后选择,例如:

    const numbers = [1, 2, 3, 4, 5];
    const listItems = numbers.map((numbers,index) =>
      <li key={index}>{numbers}</li>
    );
    
    ReactDOM.render(
      <ul>{listItems}</ul>,
      document.getElementById('root')
    );
    
  2. 使用 keys 提取组件 keys 只在数组的上下文中存在意义。例如,如果你提取 一个 ListItem 组件,应该把 key 放置在数组处理的 <ListItem /> 元素中,不能放在 ListItem 组件自身中的 <li> 根元素上。

    例如:

    function ListItem(props) {
      const value = props.value;
      return (
        // 错误!不需要在这里指定 key:
        <li key={value.toString()}>
          {value}
        </li>
      );
    }
    
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map((number) =>
        // 错误!key 应该在这里指定:
        <ListItem value={number} />
      );
      return (
        <ul>
          {listItems}
        </ul>
      );
    }
    
    const numbers = [1, 2, 3, 4, 5];
    ReactDOM.render(
      <NumberList numbers={numbers} />,
      document.getElementById('root')
    );
    

    一个好的经验准则是:元素中调用 map() 需要 keys

  3. keys同辈元素中必须是唯一

    在数组中使用的 keys 必须在它们的同辈之间唯一。然而它们并不需要全局唯一。我们可以在操作两个不同数组的时候使用相同的 keys。

表单(Forms)

  1. 受控组件(Controlled Components)

    在 HTML 中,表单元素如 <input><textarea><select> 表单元素通常保持自己的状态,并根据用户输入进行更新。而在 React 中,可变状态一般保存在组件的 state(状态) 属性中,并且只能通过 setState() 更新。

    我们可以通过使 React 的 state 成为 “单一数据源原则” 来结合这两个形式。然后渲染表单的 React 组件也可以控制在用户输入之后的行为。这种形式,其 值由 React 控制的输入表单元素 称为“ 受控组件 ”。

    例如,如果我们想在提交时记录名称,我们可以将表单写为受控组件:

    class NameForm extends React.Component {
      constructor(props) {
        super(props);
        this.state = {value: ''};
      }
    
      handleChange(event) {
        this.setState({value: event.target.value});
      }
    
      handleSubmit(event) {
        alert('A name was submitted: ' + this.state.value);
        event.preventDefault();
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit.bind(this)}>
            <label>
              Name:
              <input type="text" value={this.state.value} onChange={this.handleChange.bind(this)} />
            </label>
            <input type="submit" value="Submit" />
          </form>
        );
      }
    }
    
    ReactDOM.render(
      <NameForm />,
      document.getElementById('root')
    );
    

    设置表单元素的value属性之后,其显示值将由 this.state.value 决定,以满足React状态的同一数据理念。每次键盘敲击之后会执行 handleChange 方法以更新React状态,显示值也将随着用户的输入改变。

    由于 value 属性设置在我们的表单元素上,显示的值总是 this.state.value,以满足 state 状态的同一数据理念。由于 handleChange 在每次敲击键盘时运行,以更新 React state(状态),显示的值将更新为用户的输入。

    对于受控组件来说,每一次 state(状态) 变化都会伴有相关联的处理函数。这使得可以直接修改或验证用户的输入。

  2. 处理多个输入元素

    当您需要处理多个受控的 input 元素时,您可以为每个元素添加一个 name 属性,并且让处理函数根据 event.target.name 的值来选择要做什么。而不必写多个 onChange 函数

    例如:

    class Reservation extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          isGoing: true,
          numberOfGuests: 2
        };
    
        this.handleInputChange = this.handleInputChange.bind(this);
      }
    
      handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
    
        this.setState({
          [name]: value
        });
      }
    
      render() {
        return (
          <form>
            <label>
              Is going:
              <input
                name="isGoing"
                type="checkbox"
                checked={this.state.isGoing}
                onChange={this.handleInputChange} />
            </label>
            
            <label>
              Number of guests:
              <input
                name="numberOfGuests"
                type="number"
                value={this.state.numberOfGuests}
                onChange={this.handleInputChange} />
            </label>
          </form>
        );
      }
    }
    
    ReactDOM.render(
      <Reservation />,
      document.getElementById('root')
    );
    

    handleInputChange() 方法中,注意我们如何使用ES6计算的属性名称语法来更新与给定输入名称相对应的 state(状态) 键:

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

状态提升(Lifting State Up)

  1. 在一个 React 应用中,对于任何可变的数据都应该循序“单一数据源”原则。通常情况下,state 首先被添加到需要它进行渲染的组件。然后,如果其它的组件也需要它,你可以提升状态到它们最近的祖先组件。你应该依赖 从上到下的数据流向 ,而不是试图在不同的组件中同步状态。

    提升状态相对于双向绑定方法需要写更多的“模板”代码,但是有一个好处,它可以更方便的找到和隔离 bugs。由于任何 state(状态) 都 “存活” 在若干的组件中,而且可以分别对其独立修改,所以发生错误的可能大大减少。另外,你可以实现任何定制的逻辑来拒绝或者转换用户输入。

    如果某个东西可以从 props(属性) 或者 state(状态) 得到,那么它可能不应该在 state(状态) 中。

    状态提升:子组件要渲染的数据不定义在自身 state 中,而是定义在最近的公共父组件中。该数据由父组件通过 props 传递给子组件。当子组件发生改变时,触发父组件的 onChange() 方法,父组件更改 state ,引起子组件数据改变,从而达到A子组件数据变化时B子组件数据跟着变化的效果。)

组合 VS 继承(Composition vs Inheritance)

  1. 如果要在组件之间重用非 U I功能,我们建议将其提取到单独的 JavaScript 模块中。组件可以导入它并使用该函数,对象或类,而不扩展它。

React 的编程思想

  1. 如何拆分组件呢?

    其实只需要像拆分一个新方法或新对象一样的方式即可。一个常用的技巧是 单一职责原则 ,即一个组件理想情况下只处理一件事。如果一个组件持续膨胀,就应该将其拆分为多个更小的组件中。


写在最后:强烈建议自己读一遍 React中文文档 (有些翻译错误,不影响阅读,有能力可以去读原版。 O(∩_∩)O哈哈~)