首页» 前端 » react » 二、React基础精讲:编写TodoList、事件绑定、JSX语法、组件之间传值

二、React基础精讲:编写TodoList、事件绑定、JSX语法、组件之间传值

日期:2019年05月29日 阅读次数:1614 分类:react

一、使用React编写TodoList功能

JSX语法:render返回元素最外层必须由一个元素包裹。
Fragment 可以作为React的最外层元素占位符。

import React, {Component, Fragment} from 'react';

class TodoList extends Component {
  render() {
    return (
      <Fragment>
        <div>
          <input/>
          <button>提交</button>
        </div>
        <ul>
          <li>1111111</li>
          <li>2222222</li>
          <li>3333333</li>
        </ul>
      </Fragment>
    )
  }
}

export default TodoList;

二、React 中的响应式设计思想和事件绑定

  1. React在创建实例的时候, constructor(){} 是最先执行的;
  2. this.state 负责存储数据;
  3. 如果修改state中的内容,不能直接改,需要通过setState向里面传入对象的形式进行修改;
  4. JSX中js表达式用{}包裹;
  5. 事件绑定需要通过bind.(this)对函数的作用域进行变更;
import React, {Component, Fragment} from 'react';

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: []
    }
  }

  render() {
    return (
      <Fragment>
        <div>
          <input value={this.state.inputValue} onChange={this.handleChange.bind(this)}/>
          <button>提交</button>
        </div>
        <ul>
          <li>1111111</li>
          <li>2222222</li>
          <li>3333333</li>
        </ul>
      </Fragment>
    )
  }

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

export default TodoList;

三、实现 TodoList 新增删除功能

import React, {Component, Fragment} from 'react';

lass TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: ['学习英语', '学习React']
    }
  }

  render() {
    return (
      <Fragment>
        <div>
          <input value={this.state.inputValue} onChange={this.handleChange.bind(this)}/>
          <button onClick={this.handleBtnClick.bind(this)}>提交</button>
        </div>
        <ul>
          {this.state.list.map((item, index) => {
            return <li key={index} onClick={this.handleItemDelete.bind(this, index)}>{item}</li>
          })}
        </ul>
      </Fragment>
    )
  }
  handleChange (e) {
    this.setState({
      inputValue: e.target.value
    })
  }

  // 点击提交后,列表中添加一项,input框中内容清空
  handleBtnClick () {
    this.setState({
      list: [...this.state.list, this.state.inputValue],
      inputValue: ''
    })
  }

  // 删除
  handleItemDelete(index) {
    let list = [...this.state.list]; // 拷贝一份原数组,因为是对象,所以不能直接赋值,会有引用问题
    list.splice(index, 1);

    this.setState({
      list: list
    })

    // 以下方法可以生效,但是不建议使用。
    // React中immutable的概念:  state 不允许直接操作改变,否则会影响性能优化部分。

    // this.state.list.splice(index, 1);
    // this.setState({
    //   list: this.state.list
    // })
  }
}

export default TodoList;

四、JSX语法细节补充

1、在jsx语法内部添加注释:

  {/*这里是注释*/}

或者:

{
  //这里是注释
}

2、JSX语法中的属性不能和js中自带的属性和方法名冲突

元素属性class 替换成 className
lable标签中的for 替换成 htmlFor

3、解析html内容

如果需要在JSX里面解析html的话,可以在标签上加上属性dangerouslySetInnerHTML属性(标签中不需要再输出item):如dangerouslySetInnerHTML={{__html: item}}

...

render() {
  return (
    <Fragment>
      {/* 这是一个注释 */}
      {
        // class 换成 className
        // for 换成 htmlFor
      }
      <div className="input">
        <lable htmlFor={"insertArea"}>请输入内容</lable>
        <input id="insertArea" value={this.state.inputValue} onChange={this.handleChange.bind(this)}/>
        <button onClick={this.handleBtnClick.bind(this)}>提交</button>
      </div>
      <ul>
        {this.state.list.map((item, index) => {
          return (
            <li
              key={index}
              onClick={this.handleItemDelete.bind(this, index)}
              dangerouslySetInnerHTML={{__html: item}}
            >
            </li>

            // <li
            //   key={index}
            //   onClick={this.handleItemDelete.bind(this, index)}
            // >
            //   {item}
            // </li>
          )
        })}
      </ul>
    </Fragment>
  )
}

...

五、拆分组件与组件之间的传值

父子组件之间通讯:

①父=>子

父组件通过属性向子组件传递数据,子组件通过this.props.属性名 获取父组件传来的数据。

②子=>父

子组件调用父组件的方法来改变父组件的数据。也是父组件通过属性把父组件对应的方法传递给子组件(在父组件向子组件传入方法时,就要绑定this,不然在子组件找不到方法),然后在子组件中通过this.props.方法名(属性名) 调用对应的父组件的方法并传递对应的参数。通过触发父组件方法改变数据,数据改变从而重新渲染页面。

父组件(todoList.js)
import React, {Component, Fragment} from 'react';
import TodoItem from './todoItem';

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: ['学习英语', '学习React']
    }
  }

  render() {
    return (
      <Fragment>
        <div className="input">
          <label htmlFor={"insertArea"}>请输入内容</label>
          <input id="insertArea" value={this.state.inputValue} onChange={this.handleChange.bind(this)}/>
          <button onClick={this.handleBtnClick.bind(this)}>提交</button>
        </div>
        <ul>
          {this.state.list.map((item, index) => {
            return (
              <TodoItem
                key={index}
                index={index}
                item={item}
                deleteItem={this.handleItemDelete.bind(this)}
              />
            )
          })}
        </ul>
      </Fragment>
    )
  }

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

  // 点击提交后,列表中添加一项,input框中内容清空
  handleBtnClick () {
    this.setState({
      list: [...this.state.list, this.state.inputValue],
      inputValue: ''
    })
  }

  // 删除
  handleItemDelete(index) {
    let list = [...this.state.list];
    list.splice(index, 1);

    this.setState({
      list: list
    })
  }
}

export default TodoList;
子组件(todoItem.js)
import React, { Component } from 'react';

class TodoItem extends Component {
  constructor(props) {
    super(props);
    this.handleDeleteItem = this.handleDeleteItem.bind(this);
  }

  render() {
    return (
      <li onClick={this.handleDeleteItem}>
        {this.props.item}
      </li>
    )
  }

  handleDeleteItem() {
    this.props.deleteItem(this.props.index);
  }
}

export default TodoItem;

六、TodoList 代码优化

  1. 事件方法的this指向要在constructor里面统一进行绑定,这样可以优化性能,如:this.fn = this.fn.bind(this)
  2. setState在新版的react中写成:this.setState(()=>{retuen {}}) 或 this.setState(()=>({}))。第一中写法可以在return前写js逻辑,新版的写法有一个参数prevState,可以代替修改前的this.state,同样是可以提高性能,也能避免不小心修改state导致的bug。
  3. JSX中也可以把某一块代码提出来,直接定义一个方法把内容return出来,再在JSX中引用这个方法。以达到拆分代码的目的。
import React, {Component, Fragment} from 'react';
import TodoItem from './todoItem';

class TodoList extends Component {
  constructor(props) {
    super(props);
    this.state = {
      inputValue: '',
      list: ['学习英语', '学习React']
    }

    // 统一在constructor中绑定this,提交性能
    this.handleChange = this.handleChange.bind(this);
    this.handleBtnClick = this.handleBtnClick.bind(this);
    this.handleItemDelete = this.handleItemDelete.bind(this);
    this.getTodoItem = this.getTodoItem.bind(this);
  }

  render() {
    return (
      <Fragment>
        <div className="input">
          <label htmlFor={"insertArea"}>请输入内容</label>
          <input id="insertArea" value={this.state.inputValue} onChange={this.handleChange}/>
          <button onClick={this.handleBtnClick}>提交</button>
        </div>
        <ul>
          {this.getTodoItem()}
        </ul>
      </Fragment>
    )
  }

  handleChange (e) {
    // this.setState({
    //   inputValue: e.target.value
    // })

    // 因这种写法setState是异步的,有时e.target获取不到,所以先赋值给一个变量再使用。
    const value = e.target.value;
    // 新版写法,setState不但可以接受一个对象,也可以接受一个方法
    // this.setState(() => {
    //   return {
    //     inputValue: value
    //   }
    // })

    // this.setState(()=>{retuen {}}) 简写成 this.setState(()=>({}))
    // 还可以再简写成
    this.setState(() => (
      {
        inputValue: value
      }
    ))
  }

  // 点击提交后,列表中添加一项,input框中内容清空
  handleBtnClick () {
    // this.setState({
    //   list: [...this.state.list, this.state.inputValue],
    //   inputValue: ''
    // })

    // 新版写法,可以使用prevState代替修改前的this.state,不但可以提高性能,也能避免不小心修改state导致的bug。
    this.setState((prevState) => {
      return {
        list: [...prevState.list, prevState.inputValue],
        inputValue: ''
      }
    })
  }

  // 删除
  handleItemDelete(index) {
    // let list = [...this.state.list];
    // list.splice(index, 1);

    // this.setState({
    //   list: list
    // })

    // 新版写法,可以在return前写js逻辑
    this.setState(() => {
      let list = [...this.state.list];
      list.splice(index, 1);
      return {list: list}
    })
  }

  // 把循环提取出来,放在一个方法中
  getTodoItem () {
    return this.state.list.map((item, index) => {
      return (
        <TodoItem key={index} index={index} item={item} deleteItem={this.handleItemDelete}/>
      )
    })
  }
}

export default TodoList;

七、围绕 React 衍生出的思考

1、声明式开发
可减少大量的dom操作;
对应的是命令式开发,比如jquery,操作DOM。

2、可以与其它框架并存
React可以与jquery、angular、vue等框架并存,在index.html页面,React只渲染指定id的div(如:root),只有这个div跟react有关系。

3、组件化
继承Component,组件名称第一个字母大写。

4、单向数据流
父组件可以向子组件传递数据,但子组件绝对不能改变该数据(应该调用父级传入的方法修改该数据)。

5、视图层框架
在大型项目中,只用react远远不够,一般用它来搭建视图,在作组件传值时要引入一些框架(Fux、Redux等数据层框架);

6、函数式编程
用react做出来的项目更容易作前端的自动化测试。

文章标签: