React 学习笔记

React 学习笔记

特点

  • 声明式
  • 基于组件

入门

基本使用

html

<div id="root"></div>

js

/*
* 创建 react 元素
* 1. 元素名称
* 2. 元素属性
* 3. 第三个及其以后的参数都是元素的子节点
*/
const title = React.createElement("h1", null,  "hello react")

/*
* 1. 要渲染的 react 元素
* 2. 挂载点
*/
ReactDOM.render(title, document.getElementById("root"))

给添加属性

const title = React.createElement("h1", { title: '我是标题', id: 'p1' },  "hello react")

元素属性里添加子节点

const title = React.createElement("h1", { title: '我是标题', id: 'p1', React.createElement('span', null, '我是 span 节点') },  "hello react")

脚手架使用 React

  • 导入 react 和 react-dom
  • 调用 React.createElement() 创建 react 元素
  • 调用 ReactDOM.render() 渲染 react 元素到页面中
import React from 'react'
import ReactDOM from 'react-dom'

const title = React.createElement('h1', null, 'hello react')
ReactDOM.render(title, document.getElementById('root'))

JSX

注意点:
    React元素的属性名使用驼峰命名法
    特殊属性名: class->className   for->htmlFor    tabindex->tabIndex
    没有子节点的 React 元素可以用 /> 结束,比如 <span /> 表示 <span></span>
    推荐使用小括号包裹 JSX

概述

  • createElement() 不好用,不直观
  • JSX 避免了这个缺点
  • JSX 和 html 几乎一样
  • JSX = JavaScript XML

基本使用

import React from 'react'
import ReactDOM from 'react-dom'

const title = <h1> Hello JSX </h1>
ReactDOM.render(title, document.getElementById('root'))
import React from 'react'
import ReactDOM from 'react-dom'

const dv = (
    <h1> hello dv </h1>
)

const title = <h1 className="title"> Hello JSX </h1>
ReactDOM.render(title, document.getElementById('root'))

使用 Js 表达式

const name = 'Jack'
const dv = (
    <h1> my name is {name}
)

条件渲染

const isLoading = true
const loadData = () => {
    if (isLoading) {
        return <div>loading...</div>
    }
    return <div>success</div>
}

const _loadData = () => {
    return isLoading ? (<div>loading...</div>) : (<div>success</div>)
}

const __loadData = () => {
    return isLoading && (<div>loading...</div>)
}

const title = (
    <h1>{loadData()}</h1>
)

列表渲染

const songs = [
    {id: 1, name: 'one'},
    {id: 2, name: 'two'}
]

const list = (
    <ul>
        {songs.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
)

样式处理

import './css/index.css'

// 行内样式
const line = (
    <h1 style={{color: 'red', backgroundColor: 'white'}}>
        行内样式
    </h1>
)

// 使用类名的方式添加样式
const line2 = (
    <h2 className="title"></h2>
)

组件

组件创建

  1. 使用函数创建
  2. 使用类创建组件
  • 使用函数创建组件
  1. 函数名称必须以大写字母开头
  2. 函数组件必须有返回值,表示该组件的结构
  3. 如果返回值为 null,表示不渲染任何内容
function Hello() {
    return (
        <div>函数组件</div>
    )
}

/**
 * 简洁一点的写法
 * const Hello = () => <div>函数组件</div>
 * /

ReactDOM.render(<Hello />, root)
  • 使用类创建组件

类组件: 使用 ES6 的 class 创建的组件

  1. 类名称必须大写字母开头
  2. 类组件应继承 ReactComponent 父类,从而可以使用父类中提供的方法或属性
  3. 类组件必须提供 render()
  4. render() 必须有返回值,表示该组件结构
class Hello extends React.Component {
    render() {
        return (
            <div>类组件</div>
        )
    }
}

ReactDOM.render(<Hello />, root)

组件抽离为单独文件

  1. 创建 Hello.js
  2. 在 Hello.js 中导入 React
  3. 创建组件
  4. 在 Hello.js 中导出该组件
    5 在 index.js 中导出 Hello 组件
  5. 渲染组件

Hello.js

import React from 'react'

class Hello extends React.Component {
    render() {
        return (
            <div>抽离组件</div>
        )
    }
}

export default Hello

index.js

import React from 'react'
import ReactDOM from 'react-dom'

import Hello from './Hello'

ReactDOM.render(<Hello />, root)

事件处理

React 事件处理与 DOM 类似

语法: on+事件名称={事件处理程序},如 onClick={()=>{}}

React 事件采用驼峰命名法,比如 onClick、onFocus

简单 demo

class App extends React.Component {
    handle() {
        console.log('单击事件触发了')
    }
    render() {
        return (
            <button onClick={this.handle}>click me</button>
        )
    }
}

事件对象

可以通过事件处理程序的参数获取事件对象

React 中的事件对象叫做: 合成事件(对象)

合成事件:兼容所有游览器,无需担心跨游览器兼容性问题

class App extends React.Component {
    handle(e) {
        // 阻止游览器默认行为
        e.preventDefault()
        console.log('a 标签点击了')
    }
    render() {
        return (
            <a href="http://itcast.cn" onClick={this.handle}>传智</button>
        )
    }
}

有状态组件和无状态组件

函数组件叫无状态组件,类组件又叫做有状态组件

状态(state)即数组

函数组件没有自己的状态,只负责数据展示(静)

类组件有自己的状态,负责更新 UI ,让页面动起来

  • state 基本使用

状态 state 即数据,是组件内部的私有数据,只能在组件内部使用

state 的值是对象,表示一个组件中可以有多个数据

状态是私有的,只能在组件内部使用

通过 this.state 获取状态

class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
    }
    /**
     * 简化语法
     * state = {
     *  count: 0
     * }
    */

    handle(e) {
        // 阻止游览器默认行为
        e.preventDefault()
        console.log('a 标签点击了')
    }
    render() {
        /**
         * 请不要直接修改, this.state.count += 1 是错误的
        */
        return (
            <h1>计数器 {this.state.count}</h1>
            <button onClick={() => {
                this.setState({count: this.state.count +1})
            }}>+1</button>
        )
    }
}

从 JSX 中抽离事件处理程序

  • 如果 JSX 中掺杂了过多 js 代码,会显得非常混乱
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
    }

    incr(e) {
        // 事件处理程序的 this 是 undefined
        this.setState({count: this.state.count + 1})
    }
    render() {
        /**
         * 请不要直接修改, this.state.count += 1 是错误的
        */
        return (
            <h1>计数器 {this.state.count}</h1>
            // c 这是不行的
            /**
             * 箭头函数自身是不绑定 this 的,因此可写成 onClick={() => this.incr()}
            */

           /**
            * 利用 es5 的 bind(),将事件处理程序中的 this 与组件实例绑定到一起
            * 需要在 constructor() 中改动
            * constructor() {
            *   super()
            *   this.incr = this.incr.bind(this)
            * }
            * 
            * 这样后就可以 <button onClick={this.incr}>+1</button>
           */
          
          /**
           * 事件处理程序高阶用法
           * incr = () => {
           *    this.setState({count: this.state.count + 1})
           * }
          */
        )
    }
}

表单处理

表单是可输入的,也就是有自己的可变状态

React 希望所有的可变状态都保存到 state 中,并且只能通过 setState() 修改

  • 受控组件
class App extends React.Component {
    state = {
        txt: '',
        city: 'bj'
    }

    /**
     * 处理 textarea、select 也是这样的
     * 处理复选框流程是为每个选项设置一个 state ,通过检查 state 确认是否选中,但属性是 checked
    */
    handleChange = e => {
        this.setState({
            txt: e.target.value
        })
    }

    handleCity = e => {
        this.setState({
            city: e.target.value
        })
    }

    render() {
        return (
            <div>
                <input type="text" value={this.state.txt} onChange={this.handleChange}>
                <select value={this.state.city} onChange={this.handleCity}>
            </div>
        )
    }
}

万能表单处理 handle,但需要为每个表单项添加 name 属性
这能简化代码量

handle = e => {
    const target = e.target
    
    const value = target.type == 'checkbox' ? target.checked : target.value
    const name = target = name
    this.setState({
        [name]: value
    })
}
  • 非受控组件

借助于 ref,使用元素 DOM 方式来获取表单元素值

ref 作用: 获取 DOM 或组件

class App extends React.Component {
    constructor() {
        super()
        this.txtRef = React.createRef()
    }

    getTxt = () => {
        console.log(this.txtRef.current.value)
    }

    render() {
        return (
            <div>
                <input type="text" ref={this.txtRef} />
                <button onClick={this.getTxt}>获取文本框的值</button>
            </div>
        )
    }
}

组件通讯

组件是封闭的,要接收组件的数据应该通过 props 来实现

props 作用: 接收传递给组件的数据

传递数据: 函数组件通过参数 props 来传递数据,类组件通过 this.props 接收数据

props 是只读对象,不能对其进行修改

  • 函数接收数据
function Hello(props) {
    console.log(props)
    return (
        <div>{props.name}</div>
    )
}

/**
 * 使用: <Hello name="jack">
*/
  • 类接收数据
class Hello extends React.Component {
    render() {
        return (
            <div>{this.props.age}</div>
        )
    }
}

/**
 * 使用: <Hello age={19}>
*/

注意,如果使用类组件时写了构造函数,应该把 props 传递给 super(),否则无法在构造函数中获取到 props(注意,不影响 render(),只对构造函数有影响)

class Hello extends React.Component {
    constructor(props) {
        super(props)
        console.log(props)
    }
    
    render() {
        return null
    }
}
三种通讯方式
  • 父组件传递数据给子组件
class Parent extends React.Component {
    state = {
        lastName: 'wang'
    }

    render() {
        return (
            <Child name={this.state.lastName}>
        )
    }
}

const Child = props => {
    return (
        子组件接收到的数据是: {props.name}
    )
}
  • 子组件传递数据给父组件

思路: 利用回调函数,子组件调用,将要传递的数据作为回调函数的参数

class Parent extends React.Component {
    getChildMsg = data => {
        console.log(data)
    }

    render() {
        return (
            <Child getMsg={this.getChildMsg} />
        )
    }
}

class Child extends React.Component {
    state = {
        msg: '刷抖音'
    }

    click = () {
        this.props.getMsg(this.state.msg)
    }

    render() {
        return (
            <button onClick={this.click}>点我,向父组件传递数据</button>
        )
    }
}
  • 兄弟组件通讯

将共享组件提升到最近的公共父组件中,由公共父组件管理这个状态

思想: 状态提升

// 父组件
class Counter extends React.Component {
    state = {
        count: 0
    }

    // 提供修改状态的方法
    onIncr() = () => {
        this.setState({
            count: this.state.count + 1
        })
    }

    render() {
        return (
            <Child1 count={this.state.count} />
            <Child2 onIncr={this.onIncr} />
        )
    }
}

const Child1 = props => {
    return <h1>计数器: {props.count}
}

const Child2 = props => {
    return <button onClick={() => props.incr()}>+1<button>
}
多层组件传递数据
const { Provider, Consumer } = React.createContext()

class App extends React.Component {
    render() {
        <Provider value="pink">
            <Node />
        </Provider>
    }
}

const Node = props => {
    /**
     * 虽然写法怪异,但还是得按照他的要求来写
    */
    return (
        <Consumer>
            {
                data => <span>我是子节点 -- {data}</span>
            }
        </Consumer>
    )
}

props 属性深入

children 属性

当组件标签有子节点时才会有 children 属性

const App = props => {
    return (
        <div>
            <h1>组件标签的子节点:</h1>
            {props.children}
        </div>
    )
}

ReactDOM.render(<App>我是子节点</App>, document.getElementById('root'))

props 默认值

const App = props => {
    return (
        <h1>此处展示 props 的默认值: {props.pageSize}
    )
}

App.defaultProps = {
    pageSize: 10
}

ReactDOM.render(<App />, document.getElementById('root'))

声明周期

  • 创建时
    constructor() -> render() -> componentDidMount()

  • 组件更新时机

    1. New props
    1. setState()
    1. forceUpdate()

React 路由

现代的前端应用大多是 SPA (单页应用程序)

前端路由是一套映射规则,在 React 中,是 URL路径 与 组件 之间的关系

基本使用

  1. 安装 react-router-dom
  2. 导入路由的三个核心组件: Router/Route/Link
  3. 使用 Router 组件包裹整个应用
  4. 使用 Link 组件作为导航菜单(路由入口)
  5. 使用 Route 组件配置路由规则和要展示的组件(路由出口)
  • 安装
cnpm install react-router-dom --save
  • 使用
// 导入组件
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'

const First = () => <p>页面一的内容</p>

// 使用 Router 组件包裹整个应用
const App = () => (
    <Router>
        <div>
            <h1>React 路由基础</h1>
            {/* 指定路由入口 */}
            <Link to="/first">页面一</Link>

            {/* 指定路由出口 */}
            <Route path="/first" component={First}>
        </div>
    </Router>
)

编程式导航

其实就是通过 JS 代码来实现页面跳转

handle = () => {
    this.props.history.push('/home')
}

返回 n 个页面(整数或负数)

this.props.history.go(1)

默认路由

<Route path="/" component={Home}>

路由匹配模式

默认情况下,React 使用模糊匹配模式
给 Route 组件添加 exact 属性,让其变为精确匹配模式

<Route exact path="/" component={Home}>
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值