React基本使用

201 阅读6分钟

自己用create-react-app 创建项目

1、JSX基本使用

1.1、变量、表达式

// 获取变量 插值
   const pElem = <p>{this.state.name}</p>
   return pElem
// 表达式
   const exprElem = <p>{this.state.flag ? 'yes' : 'no'}</p>
   return exprElem

1.2、class style

class写成className

// class
        const classElem = <p className="title">设置 css class</p>
        return classElem
 // style
        const styleData = { fontSize: '30px',  color: 'blue' }
        const styleElem = <p style={styleData}>设置 style</p>
        // 内联写法,注意 {{ 和 }}
        // const styleElem = <p style={{ fontSize: '30px',  color: 'blue' }}>设置 style</p>
        return styleElem

1.3、子元素和组件

// 子元素
        const imgElem = <div>
            <p>我的头像</p>
            <img src="xxxx.png"/>
            <img src={this.state.imgUrl}/>
        </div>
        return imgElem
// 加载组件
        const componentElem = <div>
            <p>JSX 中加载一个组件</p>
            <hr/>
            <List/>
        </div>
        return componentElem

1.4、条件

if else

// if else
        if (this.state.theme === 'black') {
            return blackBtn
        } else {
            return whiteBtn
        }

三元表达式

// 三元运算符
        return <div>
            { this.state.theme === 'black' ? blackBtn : whiteBtn }
        </div>

逻辑运算符 && ||

return <div>
            { this.state.theme === 'black' && blackBtn }
        </div>

1.5、列表渲染

map

return <ul>
            { /* vue v-for */
                this.state.list.map(
                    (item, index) => {
                        // 这里的 key 和 Vue 的 key 类似,必填,不能是 index 或 random
                        return <li key={item.id}>
                            index {index}; id {item.id}; title {item.title}
                        </li>
                    }
                )
            }
        </ul>

key

2、事件

1、bind this

this - 使用 bind

1、在contructor中提前bind绑定,性能更好,因为他只执行一次即可

this.clickHandler1 = this.clickHandler1.bind(this)

constructor(props) {
    super(props)
    this.state = {
        name: 'zhangsan',
    }
    // 修改方法的 this 指向
    this.clickHandler1 = this.clickHandler1.bind(this)
}
render() {
    // this - 使用 bind
    return <p onClick={this.clickHandler1}>
        {this.state.name}
    </p>
}
clickHandler1() {
    // console.log('this....', this) // this 默认是 undefined
    this.setState({
        name: 'lisi'
    })
}

2、点击时候再绑定,性能比较差,每点击一次就会绑定一次

render() {
    // this - 使用 bind
    return <p onClick={this.clickHandler1.bind(this)}>
        {this.state.name}
    </p>
    }
clickHandler1() {
    // console.log('this....', this) // this 默认是 undefined
    this.setState({
        name: 'lisi'
    })
}

3、静态方法,this 指向当前实例

// 静态方法,this 指向当前实例
    clickHandler2 = () => {
        this.setState({
            name: 'lisi'
        })
    }

2、关于event参数

1. event 是 SyntheticEvent 合成事件,模拟出来 DOM 事件所有能力

2. event.nativeEvent 是原生事件对象

3. 所有的事件,都被挂载到 document上。(event.nativeEvent.currentTarget可以查看到)

4. 和 DOM 事件不一样,和 Vue 事件也不一样

render() {
    return <a href="https://imooc.com/" onClick={this.clickHandler3}>
            //     click me
            // </a>
}
// 获取 event
clickHandler3 = (event) => {
    event.preventDefault() // 阻止默认行为
    event.stopPropagation() // 阻止冒泡
    console.log('target', event.target) // 指向当前元素,即当前元素触发
    console.log('current target', event.currentTarget) // 指向当前元素,假象!!!

    // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件
    console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent
    console.log('event.__proto__.constructor', event.__proto__.constructor)

    // 原生 event 如下。其 __proto__.constructor 是 MouseEvent
    console.log('nativeEvent', event.nativeEvent)
    console.log('nativeEvent target', event.nativeEvent.target)  // 指向当前元素,即当前元素触发
    console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!

    
}

3、传递自定义参数

定义完一个函数之后,他最后都会追加一个参数,即可接受event

render() {
    return <ul>{this.state.list.map((item, index) => {
        return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>
            index {index}; title {item.title}
        </li>
    })}</ul>
}
// 传递参数
    clickHandler4(id, title, event) {
        console.log(id, title)
        console.log('event', event) // 最后追加一个参数,即可接收 event
    }
import React from 'react'

class EventDemo extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            name: 'zhangsan',
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ]
        }

        // 修改方法的 this 指向
        this.clickHandler1 = this.clickHandler1.bind(this)
    }
    render() {
        // // this - 使用 bind
        // return <p onClick={this.clickHandler1}>
        //     {this.state.name}
        // </p>

        // // this - 使用静态方法
        // return <p onClick={this.clickHandler2}>
        //     clickHandler2 {this.state.name}
        // </p>

        // // event
        // return <a href="https://imooc.com/" onClick={this.clickHandler3}>
        //     click me
        // </a>

        // 传递参数 - 用 bind(this, a, b)
        return <ul>{this.state.list.map((item, index) => {
            return <li key={item.id} onClick={this.clickHandler4.bind(this, item.id, item.title)}>
                index {index}; title {item.title}
            </li>
        })}</ul>
    }
    clickHandler1() {
        // console.log('this....', this) // this 默认是 undefined
        this.setState({
            name: 'lisi'
        })
    }
    // 静态方法,this 指向当前实例
    clickHandler2 = () => {
        this.setState({
            name: 'lisi'
        })
    }
    // 获取 event
    clickHandler3 = (event) => {
        event.preventDefault() // 阻止默认行为
        event.stopPropagation() // 阻止冒泡
        console.log('target', event.target) // 指向当前元素,即当前元素触发
        console.log('current target', event.currentTarget) // 指向当前元素,假象!!!

        // 注意,event 其实是 React 封装的。可以看 __proto__.constructor 是 SyntheticEvent 组合事件
        console.log('event', event) // 不是原生的 Event ,原生的 MouseEvent
        console.log('event.__proto__.constructor', event.__proto__.constructor)

        // 原生 event 如下。其 __proto__.constructor 是 MouseEvent
        console.log('nativeEvent', event.nativeEvent)
        console.log('nativeEvent target', event.nativeEvent.target)  // 指向当前元素,即当前元素触发
        console.log('nativeEvent current target', event.nativeEvent.currentTarget) // 指向 document !!!

        // 1. event 是 SyntheticEvent ,模拟出来 DOM 事件所有能力
        // 2. event.nativeEvent 是原生事件对象
        // 3. 所有的事件,都被挂载到 document 上
        // 4. 和 DOM 事件不一样,和 Vue 事件也不一样
    }
    // 传递参数
    clickHandler4(id, title, event) {
        console.log(id, title)
        console.log('event', event) // 最后追加一个参数,即可接收 event
    }
}

export default EventDemo

3、表单

input textarea select 用value

checkbox radio 用checked

受控组件 类似于双向绑定的意思,在react中要手动onChange去改变state的值

render() {
    return <div>
        <p>{this.state.name}</p>
        <label htmlFor="inputName">姓名:</label> {/* 用 htmlFor 代替 for */}
        <input id="inputName" value={this.state.name} onChange={this.onInputChange}/>
    </div>
}  
onInputChange = (e) => {
    this.setState({
        name: e.target.value
    })
}

textarea - 使用 value

return <div>
    <textarea value={this.state.info} onChange={this.onTextareaChange}/>
    <p>{this.state.info}</p>
</div>

select - 使用 value

return <div>
    <select value={this.state.city} onChange={this.onSelectChange}>
        <option value="beijing">北京</option>
        <option value="shanghai">上海</option>
        <option value="shenzhen">深圳</option>
    </select>
    <p>{this.state.city}</p>
</div>

checkbox

return <div>
    <input type="checkbox" checked={this.state.flag} onChange={this.onCheckboxChange}/>
    <p>{this.state.flag.toString()}</p>
</div>

radio

return <div>
    male <input type="radio" name="gender" value="male" checked={this.state.gender === 'male'} onChange={this.onRadioChange}/>
    female <input type="radio" name="gender" value="female" checked={this.state.gender === 'female'} onChange={this.onRadioChange}/>
    <p>{this.state.gender}</p>
</div>

4、组件和props(类型检测)

1、props传递数据

父传子:

<List list={this.state.list}/>

子接受:

1、直接属性方式使用:

this.props.list

2、先解构,在使用list

 const { list } = this.props

2、props传递函数

父传子:

<Input submitTitle={this.onSubmitTitle}/>

子接受:属性方式接受,方法执行并且传递参数

const { submitTitle } = this.props
submitTitle(this.state.title) 

3、props类型检查

react中的限制父传子过来的数据的类型,需要用到prop-types这个包

第一种在组件内部 使用static的方式定义


  static propTypes = {
    // name:Types.number,// 规定 name必须是一个数字
    name:Types.oneOfType([
      // 类型 可以是数字 也可以是 字符串  ;参数必传
      Types.string.isRequired,
      Types.number.isRequired
    ])
  }
  static defaultProps = {
    // 设置默认值使用的
    name:"默认值"
  }

第二种在组件外部

// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}

// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}
// props 类型检查
// child.propTypes={}

todolist案例代码

import React from 'react'
import PropTypes from 'prop-types'

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            title: ''
        }
    }
    render() {
        return <div>
            <input value={this.state.title} onChange={this.onTitleChange}/>
            <button onClick={this.onSubmit}>提交</button>
        </div>
    }
    onTitleChange = (e) => {
        this.setState({
            title: e.target.value
        })
    }
    onSubmit = () => {
        const { submitTitle } = this.props
        submitTitle(this.state.title) // 'abc'

        this.setState({
            title: ''
        })
    }
}
// props 类型检查
Input.propTypes = {
    submitTitle: PropTypes.func.isRequired
}
class List extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        const { list } = this.props

        return <ul>{list.map((item, index) => {
            return <li key={item.id}>
                <span>{item.title}</span>
            </li>
        })}</ul>
    }
}
// props 类型检查
List.propTypes = {
    list: PropTypes.arrayOf(PropTypes.object).isRequired
}
class TodoListDemo extends React.Component {
    constructor(props) {
        super(props)
        // 状态(数据)提升
        this.state = {
            list: [
                {
                    id: 'id-1',
                    title: '标题1'
                },
                {
                    id: 'id-2',
                    title: '标题2'
                },
                {
                    id: 'id-3',
                    title: '标题3'
                }
            ],
            footerInfo: '底部文字'
        }
    }
    render() {
        return <div>
            <Input submitTitle={this.onSubmitTitle}/>
            <List list={this.state.list}/>
        </div>
    }
    onSubmitTitle = (title) => {
        this.setState({
            list: this.state.list.concat({
                id: `id-${Date.now()}`,
                title
            })
        })
    }
}

export default TodoListDemo

5、state和setState

state 要在构造函数中定义

constructor(props) {
        super(props)

        // 第一,state 要在构造函数中定义
        this.state = {
            count: 0
        }
    }

1、state是不可变值、

在React中是用新的state替换旧的state,不能对state直接修改

 // this.state.count++ // 错误
        this.setState({
            count: this.state.count + 1 // SCU
        })

操作数组、对象的的常用形式

// 不可变值(函数式编程,纯函数) - 数组
const list5Copy = this.state.list5.slice()
list5Copy.splice(2, 0, 'a') // 中间插入/删除
this.setState({
    list1: this.state.list1.concat(100), // 追加
    list2: [...this.state.list2, 100], // 追加
    list3: this.state.list3.slice(0, 3), // 截取
    list4: this.state.list4.filter(item => item > 100), // 筛选
    list5: list5Copy // 其他操作
})

注意,不能直接对 this.state.list 进行 push pop splice 等,这样违反不可变值

// 不可变值 - 对象
this.setState({
    obj1: Object.assign({}, this.state.obj1, {a: 100}),
    obj2: {...this.state.obj2, a: 100}
})

注意,不能直接对 this.state.obj 进行属性设置,这样违反不可变值

2、setState可能是异步更新

2.1、直接使用是异步的,需要回调函数获取最新值

this.setState({
            count: this.state.count + 1
        })
        console.log('count', this.state.count)

这种情况下setState异步的,拿不到最新值,

怎么拿到:通过回调函数的形式拿到最新值

类似于Vue中的 $nextTick - DOM

this.setState({
            count: this.state.count + 1
        }, () => {
            // 联想 Vue $nextTick - DOM
            console.log('count by callback', this.state.count) // 回调函数中可以拿到最新的 state
        })

2.2、在setTimeout 中 setState 是同步的

setTimeout(() => {
            this.setState({
                count: this.state.count + 1
            })
            console.log('count in setTimeout', this.state.count)
        }, 0)

2.3、自己定义的 DOM 事件,setState 是同步的

    bodyClickHandler = () => {
        this.setState({
            count: this.state.count + 1
        })
        console.log('count in body event', this.state.count)
    }
    componentDidMount() {
        // 自己定义的 DOM 事件,setState 是同步的
        document.body.addEventListener('click', this.bodyClickHandler)
    }
    componentWillUnmount() {
        // 及时销毁自定义 DOM 事件
        document.body.removeEventListener('click', this.bodyClickHandler)
        // clearTimeout
    }

综上可以看出setState在异步中是同步的,在同步中是异步的

3、setState可能会被合并

1、state异步更新的话,更新前会被合并

传入对象,会被合并(类似 Object.assign )。执行结果只一次 +1

this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })
        this.setState({
            count: this.state.count + 1
        })

传入函数,不会被合并。执行结果是 +3

this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })
        this.setState((prevState, props) => {
            return {
                count: prevState.count + 1
            }
        })

6、组件生命周期

单组件生命周期

// React 组件生命周期图示 // projects.wojtekmaj.pl/react-lifec…

挂载时

DidMount -->vue中mounted 一般放ajax的请求

更新时 shouldComponentUpdate

render

DidUpdate

卸载时

WillUnmount -->vue中beforeDestroy

销毁定时器 销毁自定义事件 销毁DOM事件

父子组件生命周期,和vue一样

7、函数组件和类组件

区别

函数组件:

纯函数,输入props,输入jsx

没有实例,没有生命周期,没有state

不能扩展其他方法