《React全家桶:前端开发与实例详解》读书笔记1

1,127 阅读3分钟

第 1 章 第一个React Web应用程序

一、React组件

React组件是继承React.Component类的ES6类render()是React组件唯一必需的方法,该方法的返回值是渲染到页面的内容。

class ProductList extends React.Component {
  render() {
    return (
      <div className='ui unstackable items'>
        Hello, friend! I am a basic React component.
      </div>
    );
  }
}

ES6引入了类声明语法。ES6类是JavaScript基于原型的继承模型的语法糖。

声明React组件有两种方法:

  • ES6类;
  • 导入并使用createReactClass()方法
import createReactClass from 'create-react-class';

const HelloWorld = createReactClass({
    render() { return(<p>Hello, world!</p>) }
})

二、JSX和Babel

  • JSX:JavaScript扩展语法,类HTML语法,会编译成原生JavaScript最终渲染为浏览器中显示的HTML。
// JSX
<div className='ui items'>
  Hello, friend! I am a basic React component.
</div>

// 编译成JavaScript
React.createElement('div', {className: 'ui items'},
  'Hello, friend! I am a basic React component.'
)

// 渲染HTML

浏览器的JavaScript解析器在遇到JSX时会出错,JSX是标准JavaScript的扩展。所以可以让浏览器的JavaScript解释器使用此扩展。

  • Babel:JavaScript转译器,将ES6转译为ES5;把JSX编译成vanilla(原生) ES5 JS;通过设置type属性实现
// `type="text/babel"`表示需要Babel处理此脚本的加载,data-plugins指定使用的一个特殊的`Babel`插件
<script
  type="text/babel"
  data-plugins="transform-class-properties"
  src="./js/app.js"
></script>

三、ReactDOM.render()方法

ReactDOM来自react-dom库,需要两个参数,第一个参数是需要渲染的组件(what),第二个参数是渲染组件的位置(where):

ReactDOM.render([what], [where]);

ReactDOM.render(
  <ProductList />,
  document.getElementById('content')
);

四、数据state和prop

  • state由组件拥有,在初始化组件时调用了constructor()函数定义初始值,使用this.setState()方法更改。
  • props实现数据从父组件流向子组件,子组件通过this.props访问props,但无法修改它们。因为父组件拥有props并提供给子组件,子组件不是props的所有者,React支持单向数据流。 props可以传递基本类型、JavaScript对象、原子操作、函数等,甚至还可以传递React元素和虚拟DOM。
  • 当组件的stateprops更新时,组件会重新渲染。
// 父组件
class ProductList extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      products: [],
    };
  }

  componentDidMount() {
    this.setState({ products: Seed.products });
  }
  render() {
     const productComponents = this.state.products.map((product) => (
         <Product
            key={'product-' + product.id}
            id={product.id}
            title={product.title}
          />
    ));
    return (
     <div>{productComponents}</div>
    );
  }
}

// 子组件
class Product extends React.Component {
  render() {
    return (
      <div>{this.props.title} </div>
    );
  }
}

在JSX中,大括号是一个分隔符,它向JSX发出信号,表明大括号之间的内容是JavaScript表达式。另一个分隔符是引号,它用来表示字符串,如id='1'。

对于render()函数,React自动将this绑定到React组件类,所以当我们在组件内部编写this.props时,它将访问组件上的props属性。

五、事件传递

子组件不能修改prop,所以当事件触发时,需要向父组件传递,父组件进行数据修改。通过props传递函数是子组件与其父组件传递事件的标准方式。

// 父组件
class ProductList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      products: [],
    }; 
    this.handleProductUpVote = this.handleProductUpVote.bind(this);
  }
  componentDidMount() {
    this.setState({ products: Seed.products });
  }
  
  handleProductUpVote(productId) {
    console.log(productId + ' was upvoted.');
  }
  render() {
     const productComponents = this.state.products.map((product) => (
         <Product
            key={'product-' + product.id}
            id={product.id}
            title={product.title}
            onVote={this.handleProductUpVote}
          />
    ));
    return (
     <div>{productComponents}</div>
    );
  }
}

// 子组件
class Product extends React.Component {
  constructor(props) {
    super(props);

    this.handleUpVote = this.handleUpVote.bind(this);
  }
  handleUpVote() {
    this.props.onVote(this.props.id);
  }
  render() {
    return (
      <div onClick={this.handleUpVote}>{this.props.title} </div>
    );
  }
}

执行步骤:

  • onClick点击触发handleUpVote方法;
  • handleUpVote方法通知this.props.onVote;
  • 执行父组件handleProductUpVote()方法;

六、更新state和不变性

将state视为不可变的,只能使用this.setState()修改state,其他地方不能修改到state。

// 在ProductList组件内
handleProductUpVote(productId) {
  const nextProducts = this.state.products.map((product) => {// nextProducts为新数组
    if (product.id === productId) {
      return Object.assign({}, product, {// 将属性克隆出来,不修改到product对象
        votes: product.votes + 1,
      });
    } else {
      return product;
    }
  });
  this.setState({
    products: nextProducts,
  });
}

七、属性初始化器

属性初始化器简化了React类组件,能在Babel插件transform-class-properties中使用,

  • 使用箭头函数来自定义组件方法,确保函数内部的this能绑定到当前组件,可以删除constructor()函数,无须手动绑定调用;
  handleUpVote = () => (
    this.props.onVote(this.props.id)
  );
  • constructor()函数之外定义初始状态
class ProductList extends React.Component {
  state = {
    products: [],
  };
 }