美文网首页
Play With React

Play With React

作者: 梁某人的剑 | 来源:发表于2020-04-11 16:18 被阅读0次

本文内容根据官方文档整理

  • 初始化项目
    • 查看本机npm版本
    • create-react-app脚手架初始化代码库
    • 执行,构建与测试
  • React基础知识
    • jsx语法
    • jsx中的元素
    • 组件
    • 组件state与LifeCycle
    • 单向数据流:组件的状态可以作为props传递给子组件(React实现组件化的基础,同时带来了组件间通信的问题)
    • React事件处理
    • 组件的条件渲染
    • 列表的Key值
    • 表单的使用——受控组件

初始化项目

查看本机npm版本

npm -v
6.4.1

create-react-app脚手架初始化代码库

  • npm版本如果高于^5.2,需要使用npx命令
    • npx create-react-app play-with-react
      cd play-with-react
  • npm < 5.2,直接使用
    • create-react-app play-with-react
      cd play-with-react

执行,构建与测试

执行

yarn start

构建

yarn build
create react app默认的webpack配置会将src中的代码打包到项目根目录的build目录下,供部署使用。

测试

yarn test
create react app已经为我们内置了Facebookjs单元测试框架Jest

  • 测试文件名约束

    1. 测试文件必须放在src目录下级目录中
    2. 测试文件可以是tests文件夹下的*.js文件
    3. 测试文件可以是任何目录下的.test.js文件或者.spec.js文件
  • 聚焦与排除

    1. 约定每个测试用例写在it()
    2. 使用fit()仅执行该测试用例
    3. 使用xit()排除该测试用例
  • 测试覆盖率

    1. npm test -- --coverage
    2. 可在package.json中进行配置覆盖率限制
    {
      "jest": {
        "collectCoverageFrom": [
          "src/**/*.{js,jsx}",
          "!<rootDir>/node_modules/"
        ],
        "coverageThreshold": {
          "global": {
            "branches": 90,
            "functions": 90,
            "lines": 90,
            "statements": 90
          }
        },
        "coverageReporters": ["text"]
      }
    }
    

Reactj基础知识

jsx语法

jsx是js语言的扩展,使用它我们可以像下面这样在js代码中描述UI。

const element = <h1>Hello World!</h1>

jsx中可以嵌入js表达式

const name = 'Liang Lingrui';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);

jsx的大括号中可以包含任意合法的javascript表达式,包括函数执行表达式

const formatName = (user) => (user.firstName + ' ' + user.lastName)

const user = {
  firstName: 'Liang',
  lastName: 'Lingrui'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

jsx编译之后本身也是表js达式

const getGreeting = (user) => {
    const defaultElem = <h1>Hello, Stranger.</h1>
    const userElem = <h1>Hello, {formatName(user)}!</h1>
    if(user){
        return userElem
    }else{
        return defaultElem
    }
}

jsx中的元素

元素的主要特点

  1. React应用中的最小组成单元
  2. 元素不是组件,是组件的组成部分
  3. 可以将组件定义为元素
  4. 元素描述了我们在屏幕上会看到什么,就是html标签
  5. 元素一旦创建,就不可变
  6. 只有创建一个新的element替换才可以改变UI
  7. React的diff机制会把新的element跟之前的element比较,仅替换所需的部分

jsx中的element可以像html中一样使用属性,但是其属性名都采用驼峰命名规则

const element = <div className="box"></div>;
const element = <div tabIndex="0"></div>;
const element = <img src={user.avatarUrl}></img>;

jsx中的element可以有子元素

const element = (
  <div>
    <h1>Hello!</h1>
    <h2>Good to see you here.</h2>
  </div>
);

jsx本身具备防注入攻击的特性,所有的用户输入默认在render之前都会被转为字符串,所以需要使用跨域功能时需要手动关闭这一特性

jsx元素的本质

const element = <h1 className="greeting">Hello, world!</h1>

  • 方便我们使用js语言表现UI的语法糖,实际调用了一个工厂方法去实例化一个js对象
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);
  • 构造出来的对象数据结构:
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  }
};

组件

组件的价值在于构造出独立可复用的UI单元,其具有自己的功能,同时维护自己的状态数据。

组件的本质

  1. 在React中,组件就是函数,返回值为jsx中的emement
  2. 所有的组件必须是纯函数

定义组件的两种方式

  1. 功能(函数式)组件:一个接受props作为参数的函数,返回值是jsx中的element
const Welcome = (props) => (<h1>Hello, {props.name}</h1>)
  1. class式组件:继承于React中的Component类,render方法中返回element,ES6中的js语法糖,class的本质还是function
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

组件可以定义为元素

实际上是函数的调用,标签中的属性值都是props对象下的属性,都可以通过props访问到

const element = <Welcome name="LLR" />;
---------------------------------------
let props = {
    name : "LLR"
}
const element = Welcome(props)

组件的聚合

const Welcome = (props) => (<h1>Hello, {props.name}</h1>)

const App = (props) => (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
)

props是不可修改,Read-Only

因此,所有的函数式React组件都是纯函数。但是UI是交互式的、多变的,所以React引入了state来描述组件的表现状态。

组件state与LifeCycle

在class式的组件中才能使用state

  • 在class式的组件构造函数初始化state值
class Clock extends React.Component{
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
    }
    
    ...

更新组件状态值可以自动触发生命周期函数

Clock组件自己维护自己的状态信息

class Clock extends React.Component{
    constructor(props) {
        super(props);
        this.state = {date: new Date()};
    }
    
    tick() {
        this.setState({
          date: new Date()
        });
    }
    
    componentDidMount() {
        this.timerID = setInterval(
            () => this.tick(),
            1000
          );
    }

    componentWillUnmount() {
        clearInterval(this.timerID);
    }

    render(){
        return (
            <div>
                <h1>Hello, world!</h1>
                <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
            </div>
        )
    }
}

不要直接修改state的值

  • 直接修改state的值可能不会触发组件的生命周期函数
// Wrong
this.state.comment = 'Hello';

// Correct
this.setState({comment: 'Hello'});

React会将多个setState合并处理,props跟state可以异步更新,所以不要在setState的时候使用state/props来计算新的值

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

setState方法是patch式的更新,会替换参数中的部分,state中原有的不会改变

下面的代码在constructor中初始化了state对象包含posts跟comments两个属性,但是生命周期函数componentDidMount在调用的时候可以只更新posts属性。

constructor(props) {
  super(props);
  this.state = {
    posts: [],
    comments: []
  };
}
  
componentDidMount() {
  fetchPosts().then(response => {
    this.setState({
      posts: response.posts
    });
  });
}

单向数据流:组件的状态可以作为props传递给子组件(React实现组件化的基础,同时带来了组件间通信的问题)

如Clock案例中:

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

也适用于自定义组件的属性值传递,FormattedDate组件并不知道date属性的值是来时用户输入还是父组件状态值

const FormattedDate = (props) => (
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
)
<FormattedDate date={this.state.date} />

React事件处理

onClick属性方法可以handel元素/组件的点击事件,需要特别注意:将事件处理方法传递到子组件时要手动绑定this上下文,否则调用对象为undefined,这是js语法导致的问题,跟React无关

class Switch extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
  }

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

  render() {
    return (
    // 调用bind方法绑定时间到this指向的对象
      <button onClick={this.handleClick.bind(this)}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

使用ES6箭头函数可以避免这种绑定this的写法

class Switch extends React.Component {
  
 handleClick() {
    console.log('haha')
  }
  
  render() {
    return (
    //  使用箭头函数做事件处理
      <button onClick={(e) => this.handleClick(e)}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

组件的条件渲染

js尽量考虑函数式编程

  • 用 && 号实现 if 的效果
    • 见代码中mailBox组件
  • 使用三目运算符,来实现行内的 if else 效果
    • 见代码中LoginControl组件

列表的Key值

React的list中每一个列表的条目都应该有一个Key来做唯一表示,通常是不可变的id属性。如果缺少Key浏览器会报错。引入Key的原理请查看深入理解React相关描述

表单的使用——受控组件

  1. 为表单空间初始化state值,比如username
  2. 定义 handleChange 处理函数,里面修改 username 值
  3. render中,添加一个input使value值等于this.state.username
  4. 这个input就是一个受控组件,监听onChange事件更新用户输入保证输入数据的实时更新
class Form extends React {
  state = { username: '', email: '' }

  handleChange = event => {
    const { value, name } = event.target

    this.setState({
      [name]: value
    })
  }

  handleSubmit = e => {
    console.log(`${this.state.username} ${this.state.email}`)
    e.preventDefault()
  }

  render() {
    return (
      <div>
        Username:
        <input
          name="username"
          type="text"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <br />
        Email:
        <input
          name="email"
          type="text"
          value={this.state.email}
          onChange={this.handleChange}
        />
        <br />
        <button onClick={this.handleSubmit}>提交</button>
      </div>
    )
  }
}


相关文章

网友评论

      本文标题:Play With React

      本文链接:https://www.haomeiwen.com/subject/sipzfqtx.html