React框架的学习

什么是 React ?

  • React是一个声明式的,高效的,并且灵活的用于构建用户界面的 JavaScript 库

一、环境搭建

使用 create-react-app 快速构建 React 开发环境

create-react-app 是来自于 Facebook,通过该命令我们无需配置就能快速构建 React 开发环境。

create-react-app 自动创建的项目是基于 Webpack + ES6 。

执行以下命令创建项目:

--	安装create-react-app脚手架
npm install -g create-react-app
--	创建项目 my-app
create-react-app my-app
--	进入到项目目录
cd testdemo
--	运行项目
npm start

浏览器打开http://localhost:3000/,出现以下画面即为运行成功
在这里插入图片描述


二、react目录以及文件

目录结构

在这里插入图片描述


组件基础模板

在这里插入图片描述


三、React的知识点

1.JSX语法

class ShoppingList extends React.Componnet {
	//	虚拟DOM
    render() {
        return (
            <div className="shopping-list">
                <h1>Shoping List for {this.props.name}</h1>
                <ul>
                    <li>Instagram</li>
                    <li>WhatApp</li>
                    <li>Oculus</li>
                </ul>
            </div>
        )
    }
}

在这里,ShoppingList是一个 React组件类,或 React组件类型。组件接受参数,称为属性 props, 并通过 render方法返回一个现实的视图层次结构。

render 方法返回要渲染的内容描述,然后React接受该描述并将其渲染到屏幕上

注意:
由于 JSX 就是 JavaScript,一些标识符像 class 和 for 不建议作为 XML 属性名。作为替代,React DOM 使用 className 和 htmlFor 来做对应的属性。


自定义属性

实例中的 p 元素添加了自定义属性 data-myattribute,添加自定义属性需要使用 data- 前缀。

ReactDOM.render(
    <div>
    	<p data-myattribute="somevalue">自定义属性</p>
    </div>
    ,
    document.getElementById('example')
);

样式

React 推荐使用内联样式。
我们可以使用 camelCase 语法来设置内联样式. React 会在指定元素数字后自动添加 px 。以下实例演示了为 h1 元素添加 myStyle 内联样式:

var myStyle = {
    fontSize: 100,
    color: '#FF0000'
};
ReactDOM.render(
    <h1 style={myStyle}>菜鸟教程</h1>,
    document.getElementById('example')
);

数组

JSX 允许在模板中插入数组,数组会自动展开所有元素:

var arr = [
  <h1>菜鸟教程</h1>,
  <h2>学的不仅是技术,更是梦想!</h2>,
];	
ReactDOM.render(
  <div>{ arr }</div>,
  document.getElementById('example')
);

2.生命周期

  • componentWillMount:在渲染前调用,在客户端也在服务端。

  • componentDidMount ::在第一次渲染后调用,只在客户端。

  • componentWillReceiveProps:在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。

  • shouldComponentUpdate:返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。

  • componentWillUpdate:在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。

  • componentDidUpdate:在组件完成更新后立即调用。在初始化时不会被调用。

  • componentWillUnmount:在组件从 DOM 中移除之前立刻被调用。

在这里插入图片描述

react生命周期函数详细


3.事件处理

阻止默认行为

在 React 中不同的是你不能使用return false 的方式阻止默认行为, 你必须明确使用 preventDefault

在 React 的写法为:

function ActionLink() {
  function handleClick(e) {
    e.preventDefault();
    console.log('链接被点击');
  }
 
  return (
    <a href="#" onClick={handleClick}>
      点我
    </a>
  );
}

this指向

react中,类的方法默认是不会绑定 this 的。如果你忘记绑定 this.handleClick 并把它传入 onClick, 当你调用这个函数的时候 this 的值会是 undefined。

export default class Test extends Component {
  constructor(props) {
    super(props)
    // 该处是为了绑定当前实例对象
    this.testClick3 = this.testClick3.bind(this)
  }
  // 没有做任何绑定
  testClick() {
    console.log(this) // 打印 undefined
  }
  // 箭头函数
  testClick2 = () => {
    console.log(this) // 打印 当前实例对象
  }
  // 通过 constructor 中的 bind 绑定 this
  testClick3() {
    console.log(this) // 打印 当前实例对象
  }
  testClick4() {
    console.log(this) // 打印 当前实例对象
  }
  render() {
    return (
      <>
        <button onClick={this.testClick}>click1</button>
        <button onClick={this.testClick2}>click2</button>
        <button onClick={this.testClick3}>click3</button>
        {/* 箭头函数 => 打印 当前实例对象 */}
        <button onClick={() => this.testClick3()}>click4</button>
      </>
    )
  }
}

函数传参

通过 bind:

  • 函数名.bind(参数, Event)

通过 箭头函数:

  • (Event) => 函数名(参数, Event)
export default class Test extends Component {
  constructor() {
    super()
    this.state = { name: 'Hello world!' }
  }
  // e: 打印事件对象 | name: Hello world!
  testClick(name, e) {
    console.log(e)
    console.log(name)
  }
  render() {
    return (
      <>
        {/* 使用bind传参,第一个参数是 事件对象, 第二个才是 方法所需参数 */}
        <button onClick={this.testClick.bind(this, this.state.name)}>
          click1
        </button>
        {/* 使用箭头函数传参,(e) 是事件对象,不传入的话,方法打印出来的 e 是 undefined */}
        <button onClick={(e) => this.testClick(this.state.name, e)}>
          click2
        </button>
      </>
    )
  }
}

4.Context(上下文)

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性


API

(1)React.createContext:创建一个上下文的容器(组件), defaultValue可以设置共享的默认数据

const {Provider, Consumer} = React.createContext(defaultValue);

(2)Provider(生产者): 和他的名字一样。用于生产共享数据的地方。生产什么呢? 那就看value定义的是什么了。value:放置共享的数据。

<Provider value={/*共享的数据*/}>
    /*里面可以渲染对应的内容*/
</Provider>

(3)Consumer(消费者):这个可以理解为消费者。 他是专门消费供应商( Provider)产生数据。Consumer需要嵌套在生产者下面。才能通过回调的方式拿到共享的数据源。当然也可以单独使用,那就只能消费到上文提到的defaultValue

<Consumer>
  {value => /*根据上下文  进行渲染相应内容*/}
</Consumer>

5.组件API

设置状态:setState

setState(object nextState[, function callback])

参数说明

  • nextState,将要设置的新状态,该状态会和当前的state合并
  • callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。

合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。

关于setState
不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。

setState()并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。

setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑。


替换状态:replaceState

replaceState(object nextState[, function callback])
  • nextState,将要设置的新状态,该状态会替换当前的state
  • callback,可选参数,回调函数。该函数会在replaceState设置成功,且组件重新渲染后调用。

**replaceState()方法与setState()**类似,但是方法只会保留nextState中状态,原state不在nextState中的状态都会被删除。

设置属性:setProps

setProps(object nextProps[, function callback])
  • nextProps,将要设置的新属性,该状态会和当前的props合并
  • callback,可选参数,回调函数。该函数会在setProps设置成功,且组件重新渲染后调用。
    设置组件属性,并重新渲染组件。

props相当于组件的数据流,它总是会从父组件向下传递至所有的子组件中。当和一个外部的JavaScript应用集成时,我们可能会需要向组件传递数据或通知React.render()组件需要重新渲染,可以使用setProps()

更新组件,我可以在节点上再次调用React.render(),也可以通过**setProps()**方法改变组件属性,触发组件重新渲染。


替换属性:replaceProps

replaceProps(object nextProps[, function callback])
  • nextProps,将要设置的新属性,该属性会替换当前的props
  • callback,可选参数,回调函数。该函数会在replaceProps设置成功,且组件重新渲染后调用。
    replaceProps()方法与setProps()类似,但它会删除原有 props。

强制更新:forceUpdate

forceUpdate([function callback])

参数说明

  • callback,可选参数,回调函数。该函数会在组件**render()**方法调用后调用。

forceUpdate()方法会使组件调用自身的render()方法重新渲染组件,组件的子组件也会调用自己的render()。但是,组件重新渲染时,依然会读取this.props和this.state,如果状态没有改变,那么React只会更新DOM。

forceUpdate()方法适用于this.props和this.state之外的组件重绘(如:修改了this.state后),通过该方法通知React需要调用render()

一般来说,应该尽量避免使用forceUpdate(),而仅从this.props和this.state中读取状态并由React触发render()调用。


获取DOM节点:findDOMNode

DOMElement findDOMNode()
  • 返回值:DOM元素DOMElement

如果组件已经挂载到DOM中,该方法返回对应的本地浏览器 DOM 元素。当render返回nullfalse时,this.findDOMNode()也会返回null。从DOM 中读取值的时候,该方法很有用,如:获取表单字段的值和做一些 DOM 操作。


判断组件挂载状态:isMounted

bool isMounted()
  • 返回值:true或false,表示组件是否已挂载到DOM中

isMounted()方法用于判断组件是否已挂载到DOM中。可以使用该方法保证了setState()和forceUpdate()在异步场景下的调用不会出错。


以上参考:https://www.runoob.com/react/react-component-api.html

6.state(状态)

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。

React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。


export default class Test extends Component {
  // constructor用于给state绑定实例
  constructor(props) {
    super(props)
    // 设置state
    this.state = {
      msg: 'test msg',
      msg2: 'setState msg',
    }
  }
  // 上下两段都可用于设置state,但不能一起使用
  // state = {
  //   msg2: 'test2 msg',
  // }
  render() {
    return (
      <>
        {/* 获取state */}
        {this.state.msg}
        <button
          onClick={() => {
            this.state.msg = 'new test msg'
            // 当改变了state时,不会主动更新,需要调用this.setState()来让页面渲染,重新加载render()
            // this.setState()会更新当前页面所有的state,可以不用像msg2一样重复设置
            this.setState({
              msg2: 'setState msg',
            })
          }}
        >
          click
        </button>
        {/* {this.state.msg2} */}
      </>
    )
  }
}


处理多个state的输入

export default class Test extends Component {
  constructor() {
    super()
    this.state = {
      id: '1',
      name: '小明',
      sex: '男',
      tableList: {
        age: '22',
        height: '180cm',
      },
    }
  }

  // 处理多个输入
  // input 中的 name属性 要跟 state 的键对应
  // 处理多个input
  handleInputChange(e) {
    const target = e.target
    let name = target.name
    this.setState({
      [name]: target.value,
    })
  }

  // 处理表单中的input值
  _handleInputChange(e) {
    const target = e.target
    let name = target.name
    let value = target.value
    const tableList = this.state.tableList
    tableList[name] = value
    this.setState({
      tableList,
    })
  }

  render() {
    return (
      <>
        {this.state.id}
        <br />
        {this.state.name}
        <br />
        {this.state.sex}
        <br />
        {this.state.tableList.age}
        <br />
        {this.state.tableList.height}
        <form>
          id:
          <input
            name="id"
            value={this.state.id}
            onChange={(e) => this.handleInputChange(e)}
          />
          <br />
          name:
          <input
            name="name"
            value={this.state.name}
            onChange={(e) => this.handleInputChange(e)}
          />
          <br />
          sex:
          <input
            name="sex"
            value={this.state.sex}
            onChange={(e) => this.handleInputChange(e)}
          />
          <br />
          age:
          <input
            name="age"
            value={this.state.tableList.age}
            onChange={(e) => this._handleInputChange(e)}
          />
          <br />
          height:
          <input
            name="height"
            value={this.state.tableList.height}
            onChange={(e) => this._handleInputChange(e)}
          />
        </form>
      </>
    )
  }
}

以上处理参考:https://www.cnblogs.com/xfswy/p/14980918.html


7.组件

// class组件
export default class Test extends Component {
  render() {
    return (
      <>
        <button onClick={(e) => { console.log(e) }}>Test1 click</button>
        {/* 展开Test2,并传递子节点<h1> */}
        <Test2>
          <h1>展开hello world</h1>
        </Test2>
      </>
    )
  }
}

// 函数式组件
// 该函数是一个有效的 React 组件,因为它接收唯一带有数据的 “props”(代表属性)
// 对象与并返回一个 React 元素。这类组件被称为“函数组件”,因为它本质上就是 JavaScript 函数。
const Test2 = (props) => {
  function welcome(e) {
    console.log(e)
    console.log('hello world')
  }
  return (
    <div>
      <button onClick={(e) => welcome(e)}>Test2 click</button>
      {/* 获取传递来的子节点 */}
      {props.children}
    </div>
  )
}

8.props

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。

// 函数式组件
// 这里是通过传参获取props,所以不需要使用this
const Test2 = (props) => <div>{props.msg2}</div>

// ES6 class关键字 定义
class Test3 extends Component {
  render() {
    // 这里是通过this访问当前实例获取props
    return <div>{this.props.msg3}</div>
  }
}

class Test4 extends Component {
  // 通过静态属性 defaultProps 给props设置默认值
  static defaultProps = {
    msg4: 'default msg4',
  }
  render() {
    return <div>{this.props.msg4}</div>
  }
}

// 下方代码等价于上面Test4中的 static defaultProps
Test4.defaultProps = {
  msg4: 'new default msg4',
}

export default class Test extends Component {
  render() {
    return (
      <>
        <Test2 msg2="test msg2" />
        <Test3 msg3="test msg3" />
        <Test4 />
      </>
    )
  }
}

四、小案例

涉及以下知识点的使用

  • state:状态保持以及更新
  • 函数、事件处理
  • 列表循环
  • 条件渲染

效果展示

在这里插入图片描述

源码

写的很乱,看看就行
组件 home.js

import React, { Component } from 'react'
import './Home.css'

class Home extends Component {
  constructor(props) {
    super(props)
    this.state = {
      // 原始数据
      tableList: [
        {
          id: '031',
          name: '小明',
          sex: '男',
          age: '12',
          height: '180',
          weight: '79',
          birthday: '1922-10-20',
        },
        {.............}
      ],
      // 是否显示详细数据界面
      isShowDetailed: false,
      // 是否显示修改数据界面
      isShowEdit: false,
      // 详细数据表
      detailedItem: {},
      // 当前修改数据的key值
      editKey: null,
      // 修改数据界面模板以及存储当前需修改的数据
      editItem: {
        id: '1',
        name: '1',
        sex: '',
        age: '',
        height: '',
        weight: '',
        birthday: '',
      },
    }
    // 为函数绑定当前对象,让函数可以获取到this
    this.changeData = this.changeData.bind(this)
  }
  // 显示详细数据
  showData = (data) => {
    this.setState({
      isShowDetailed: true,
      detailedItem: data,
    })
  }
  // 隐藏详细数据
  hideData = () => {
    this.setState({
      isShowDetailed: !this.state.isShowDetailed,
    })
  }
  // 显示修改数据
  showEdit = (key) => {
    this.setState(
      {
        isShowEdit: true,
        editItem: this.state.tableList[key],
        editKey: key,
      },
      () => {
        let inputs = document
          .getElementById('editId')
          .getElementsByTagName('input')
        for (let i = 0; i < inputs.length; i++) {
          let key = Object.keys(this.state.editItem)
          inputs[i].value = this.state.editItem[key[i]]
        }
      }
    )
  }
  // 隐藏修改数据
  hideEdit = () => {
    this.setState({
      isShowEdit: !this.state.isShowEdit,
    })
  }
  // 修改数据 
  changeData() {
    let data = this.state.tableList
    data[this.state.editKey] = {
      id: document.getElementById('id').value,
      name: document.getElementById('name').value,
      sex: document.getElementById('sex').value,
      age: document.getElementById('age').value,
      height: document.getElementById('height').value,
      weight: document.getElementById('weight').value,
      birthday: document.getElementById('birthday').value,
    }
    this.setState({
      editItem: this.state.tableList[this.state.editKey],
      isShowEdit: !this.state.isShowEdit,
      isShowDetailed: !this.state.isShowDetailed,
    })
  }
  // 删除数据
  deleteData(key) {
    this.state.tableList.splice(key, 1)
    this.setState({
      isShowDetailed: false,
      isShowEdit: false,
    })
  }
  // 虚拟DOM
  render() {
    return (
      <div className="home">
        <h1>调查</h1>
        <table>
          <tbody>
            <tr>
              <th></th>
              <th>id</th>
              <th>名称</th>
              <th>性别</th>
              <th>年龄</th>
              <th>操作</th>
            </tr>
            {this.state.tableList.map((item, key) => {
              return (
                <tr key={item.id}>
                  <td>{key + 1}</td>
                  <td>{item.id}</td>
                  <td>{item.name}</td>
                  <td>{item.sex}</td>
                  <td>{item.age}</td>
                  <td>
                    <button
                      className="info"
                      onClick={this.showData.bind(this, item)}
                    >
                      详细数据
                    </button>
                    <button
                      className="warning"
                      onClick={this.showEdit.bind(this, key)}
                    >
                      编辑
                    </button>
                    <button
                      className="danger"
                      onClick={this.deleteData.bind(this, key)}
                    >
                      删除
                    </button>
                  </td>
                </tr>
              )
            })}
          </tbody>
        </table>
        <div
          className="detailed"
          style={{ display: this.state.isShowDetailed ? 'block' : 'none' }}
        >
          <span>详细数据:</span>
          <table>
            <tbody>
              <tr>
                <th>id</th>
                <th>名称</th>
                <th>性别</th>
                <th>年龄</th>
                <th>身高(cm)</th>
                <th>体重(kg)</th>
                <th>生日</th>
                <th>操作</th>
              </tr>
              <tr>
                {Object.keys(this.state.detailedItem).map((obj, idx) => {
                  return <td key={idx}>{this.state.detailedItem[obj]}</td>
                })}
                <td>
                  <button
                    className="danger"
                    onClick={this.hideData}
                    title="关闭表单"
                  >
                    X
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
        <div
          className="edit"
          id="editId"
          style={{ display: this.state.isShowEdit ? 'block' : 'none' }}
        >
          <span>编辑数据:</span>
          <table>
            <tbody>
              <tr>
                <th>id</th>
                <th>名称</th>
                <th>性别</th>
                <th>年龄</th>
                <th>身高(cm)</th>
                <th>体重(kg)</th>
                <th>生日</th>
                <th>操作</th>
              </tr>
              <tr>
                {Object.keys(this.state.editItem).map((obj, idx) => {
                  return (
                    <td key={idx}>
                      <input id={obj} />
                    </td>
                  )
                })}
                <td>
                  <button className="smbit" onClick={this.changeData}>
                    确认
                  </button>
                  <button
                    className="danger"
                    onClick={this.hideEdit}
                    title="关闭表单"
                  >
                    X
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    )
  }
}

export default Home

以上为个人笔记,不代表绝对正确,有错误还请帮忙指正

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值