React基础

本文详细介绍了ReactJS的基础知识,包括JSX语法、组件、状态管理和props的使用。讲解了虚拟DOM、事件处理、条件渲染以及列表与key的概念。此外,还探讨了生命周期方法、表单处理和非受控及受控组件。文章强调了正确使用key的重要性,以及在处理列表时避免使用index作为key的原因。

JSX的语法规则

1、全称:JavaScript XML

2、是react定义的一种类似于XML的JS扩展语法

3、 作用:用来简化创建虚拟DOM

(1)写法:var ele = <h1>Hello JSX!</h1>

(2)注意:它不是字符串,也不是HTML/XML标签,它最终产生的是一个JS对象

4、语法规则:

(1)定义虚拟DOM时,不要写引号

(2)标签中需要写JS表达式时要用{}包裹

(3)样式的类名指定不再使用class而是使用className

(4)内联样式,要用style={{key:value}}的形式来写

(5)当有很多标签的时候,需要有一个根标签将它们包裹着

(6)标签必须闭合

(7)标签首字母:

  • 若为小写字母,会将该标签转换为html中同名的标签,若html中没有该标签对应的标签元素,则会报错;
  • 若为大写字母,react会将其渲染为对应的组件,若组件未定义,则会报错;
<div>
      <h1 className='title'>Hello World</h1>
      <p style={{ "marginTop": '16px' }}>This is a simple React app</p>
      {/* 此处react将Hello当做组件,而Hello组件未声明,会报错 */}
      <Hello />
</div>

React定义组件

在React中组件分为函数组件和类组件

函数组件:

function App() {
  return (
    <div>
      <h1>Hello World</h1>
    </div>
  )
}

类组件:

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello World</h1>
      </div>
    )
  }
}

函数组件和类组件之间的区别:

  • 在写法上类组件会比函数组件多一个render,而这个render会放在该组件的原型对象上,供实例使用;
  • 函数组件没有this,而类组件不仅有this并且该this指向了这个组件的实例对象
  • 解析过程,当组件是函数组件时,react发现该组件是函数定义的,随后就会调用该函数,将返回的虚拟DOM转换为真实的DOM,随后呈现在页面上;
    当组件是类组件时,react发现该组件是类定义的,随后会new出来该类的实例,并通过该实例调用原型上的render方法,然后将render返回的虚拟DOM转换为真实DOM,随后呈现在页面上;

state属性

1、state是组件对象中最重要的属性,它的值是一个对象(可以包含多个key-value的组合)

2、组件被称为“状态机”,会通过更新组件中的state来更新对应的页面显示(即重新渲染组件)

3、state中的数据不能直接修改,需要使用setState方法来进行更新,并且更新是一种合并,不是替换

class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      name: 'John',
      age: 18
    }
  }

  // 当没有从父组件传来的props时,可以不写构造函数,此时的state能直接写在类组件中
  // 即为简写形式
  // state = {
  //   name: 'John',
  //   age: 18
  // }

  handleChangeAge = () => {
    // 通过使用setState来改变state
    this.setState({ age: this.state.age + 1 })
  }
  render() {
    const { name, age } = this.state
    return (
      <div>
        <h1>Hello {name},你今年{age}岁了</h1>
        {/* 点击按钮来改变年龄 */}
        <button onClick={this.handleChangeAge}>年龄加1</button>
      </div>
    )
  }
}

props属性

1、每个组件对象都会有props(properties)属性

2、组件标签中的所有属性都会保存在props中

3、可以通过props属性来进行组件之间的数据传递

4、无论是函数组件还是类组件,都不能修改自身的props

class App extends React.Component {

  render() {
    return (
      <div>
        <Person name='张三' age='18' sex='男' />
        <Teacher name='李老师' age='38' sex='女' />
      </div>
    )
  }
}
// 类组件使用props
class Person extends React.Component {
  constructor(props) {
    super(props)
    console.log(props);
  }
  render() {
    const { name, age, sex } = this.props
    return (
      <div>
        <h1>{name},{age},{sex}</h1>
      </div>
    )
  }
}
// 函数组件使用props
function Teacher(props) {
  const { name, sex, age } = props
  return (
    <div>
      <h1>{name},{age},{sex}</h1>
    </div>
  )
}

在这里插入图片描述
在这里插入图片描述

Refs属性

1、组件内的标签可以使用ref属性来标识自己,即访问DOM节点

何时使用Refs

  • 管理焦点,文本选择或者媒体播放
  • 触发强制性的动画
  • 集成第三方DOM库

字符串形式的ref(已过时)

<input ref='firstInput'/>

class App extends React.Component {

  showFirstData = () => {
    const { firstInput } = this.refs;
    alert(firstInput.value);
  }

  showSecondData = () => {
    const { secondInput } = this.refs;
    alert(secondInput.value);
  }

  render() {
    return (
      <div>
        <input ref='firstInput' type="text" placeholder='点击按钮提示数据' />
        <button onClick={this.showFirstData}>点击提示左侧数据</button>
        <input ref='secondInput' type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
      </div>
    )
  }
}

在这里插入图片描述

回调函数形式的ref

<input ref={(c)=>this.firstInput = c}/>

在回调函数形式的ref中,会传递一个函数。这个函数中会接收React 组件实例或者HTML DOM元素作为参数,以使它们能在其他地方被存储和访问

class App extends React.Component {

  showFirstData = () => {
    console.log(this);
    const { firstInput } = this
    alert(firstInput.value)
  }

  showSecondData = () => {
    const { secondInput } = this
    alert(secondInput.value)
  }

  render() {
    return (
      <div>
        <input ref={c => this.firstInput = c} type="text" placeholder='点击按钮提示数据' />
        <button onClick={this.showFirstData}>点击提示左侧数据</button>
        <input ref={c => this.secondInput = c} type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
      </div>
    )
  }
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/cc9bac071762421aaa4938f80262f764.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBATWFpa2Uu,size_20,color_FFFFFF,t_70,g_se,x_16

createRef的使用

firstInput = React.createRef()
<input ref={this.firstInput} />

React会在组件挂载时将current属性传入DOM元素,并在组件卸载的时候传入null值。ref会在componentDidMountcomponentDidUpdate生命周期钩子触发前更新。

class App extends React.Component {

  // 创建新的ref实例
  firstInput = React.createRef()
  secondInput = React.createRef()

  showFirstData = () => {
  //可以获得当前节点
    console.log(this.firstInput.current);
    alert(this.firstInput.current.value)
  }

  showSecondData = () => {
    alert(this.secondInput.current.value)
  }

  render() {
    return (
      <div>
        <input ref={this.firstInput} type="text" placeholder='点击按钮提示数据' />
        <button onClick={this.showFirstData}>点击提示左侧数据</button>
        <input ref={this.secondInput} type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
      </div>
    )
  }
}

在这里插入图片描述

生命周期

1、组件从创建到死亡会经历一些特定的阶段

2、React组件中包含了一系列钩子函数(生命周期回调函数),并且会在特定的时刻调用

3、我们在定义组件时,可以在一些特定的生命周期的回调函数中做一些特定的工作

在这里插入图片描述

挂载阶段:

当组件实例被创建并插入DOM中时,其生命周期调用顺序如下:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

更新阶段:

当组件中的props或者state发生变化时就会触发更新。组件更新的生命周期调用顺序如下:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

卸载:

  • componentWillUnmount()
//创建组件
class App extends React.Component {
    //构造器,通常适用于以下两种情况,如果不需要以下两种情况可以不用为组件实现该构造函数
    //通过给this.state赋值对象来初始化内部state
    //为事件处理函数绑定实例
    constructor(props) {
        console.log('Count---constructor');
        super(props)
        //初始化状态
        this.state = { count: 0 }
    }

    //加1按钮的回调
    add = () => {
        //获取原状态
        const { count } = this.state
        //更新状态
        this.setState({ count: count + 1 })
    }

    //卸载组件按钮的回调
    death = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'))
    }

    //强制更新按钮的回调
    force = () => {
        this.forceUpdate()
    }

    //若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
    static getDerivedStateFromProps(props, state) {
        console.log('getDerivedStateFromProps', props, state);
        // 若返回的是null则不会更新任何内容
        return null
    }

    //组件挂载完毕的钩子
    componentDidMount() {
        console.log('Count---componentDidMount');
    }

    //控制组件更新的“阀门”
    shouldComponentUpdate(nextProps, nextState) {
        console.log('Count---shouldComponentUpdate', nextProps, nextState);
        // 若返回值为false则不会调用componentDidUpdate()
        return true
    }

    //组件更新完毕的钩子
    componentDidUpdate(preProps, preState, snapshotValue) {
        console.log('Count---componentDidUpdate', preProps, preState, snapshotValue);
    }

    //在更新之前获取快照
    getSnapshotBeforeUpdate() {
        console.log('getSnapshotBeforeUpdate');
        // 该返回值会传递给componentDidUpdate()中的第三个参数
        return 'nihao'
    }

    //组件将要卸载的钩子
    componentWillUnmount() {
        console.log('Count---componentWillUnmount');
    }

    render() {
        console.log('Count---render');
        const { count } = this.state
        return (
            <div>
                <h2>当前求和为:{count}</h2>
                <button onClick={this.add}>点我+1</button>
                <button onClick={this.death}>卸载组件</button>
                <button onClick={this.force}>不更改任何状态中的数据,强制更新一下</button>
            </div>
        )
    }
}

页面第一次挂载:
在这里插入图片描述

点击+1更新state来更新组件:
在这里插入图片描述

强制更新:
在这里插入图片描述
组件卸载:
在这里插入图片描述

生命周期总结:

重要的钩子:

  • render:初始化渲染或者更新渲染时调用

  • componentDidMount:开启监听,发送Ajax请求

  • componentWillUnmount:做一些收尾工作,如:清理定时器

废弃的钩子:

  • componentWillMount

  • componentWillReceiveProps

  • componentWillUpdate

现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。

事件处理

1、通过onXxx属性可以指定事件处理函数(注意大小写)

  • React中使用的是自定义事件(合成)事件,而不是使用原生的DOM事件-----为了更好的兼容性
  • React中的事件是通过事件委托的方式处理的(委托给组件最外层的元素)-----为了更高效

2、通过event.target可以得到发生事件的DOM元素对象-----不要过度使用ref

class App extends React.Component {

    showFirstData = (e) => {
        console.log(e);
        console.log(e.target);
    }

    showSecondData = (e) => {
        console.log(e);
        console.log(e.target);
    }

    render() {
        return (
            <div>
                <input type="text" placeholder='点击按钮提示数据' />
                <button onClick={this.showFirstData}>点击提示左侧数据</button>
                <input type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
            </div>
        )
    }
}

在这里插入图片描述
关于事件处理的三种写法以及传值,请参考
React中的事件处理

条件渲染

在React中,可以使用JavaScript中运算符if或者条件运算符去创建元素来表现当前的状态,然后让React根据他们来更新UI

例如有两个组件,通过isLogin的真假来渲染不同的组件内容

function UserGreeting() {
    return <h1>Welcome back!</h1>;
}

function GuestGreeting() {
    return <h1>Please sign up.</h1>;
}

function App(props) {
    if (props.isLogin) {
        return <UserGreeting />;
    } else {
        return <GuestGreeting />
    }
}

与运算符&&

通过{}包裹代码,可以在JSX中写入任何表达式

例如

function App(props) {
    const unreadMessage = props.unreadMessage
    
    return (
        <div>
            <h1>Hello!</h1>
            {unreadMessage.length > 0 &&
                <h1>
                    You have {unreadMessage.length} unread messages.
                </h1>
            }
        </div>
    )
}

const messages = ['React', 'Re: React', 'Re:Re: React']

ReactDOM.render(<App unreadMessage={messages} />, document.getElementById("root"));

在JavaScript中,当true&&expression时,将会返回expression,而false&&expression时总会返回false

因此在上述例子中如果前者条件为true,则会渲染右侧的元素,若为false,则会忽略并跳过它

三目运算符

在React中不仅可以使用&&运算符来进行条件渲染,也可以使用三目运算符来完成

例如

function App(props) {
    const isLogin = props.isLogin;

    return (
        <h1>
            The user is <b>{isLogin ? 'currently' : 'not'}</b> login in
        </h1>
    )
}

列表&key

我们可以像JavaScript中的一些遍历的方法来渲染列表组件,通过使用{}在JSX中构建一个元素集合

例如

function App() {
    const numbers = [1, 2, 3, 4, 5];

    return (
        <h1>
            {numbers.map((number) => (
                <div>{number}</div>
            ))}
        </h1>
    )
}

但是这样写会有一个警告Each child in a list should have a unique "key" prop.,意思是当创建一个元素时,必须包括一个特殊的key属性

即改为:

function App() {
    const numbers = [1, 2, 3, 4, 5];

    return (
        <h1>
            {numbers.map((number) => (
                <div key={number}>{number}</div>
            ))}
        </h1>
    )
}

DOM的Diffing算法

当setState()更新状态了之后,会重新创建一个新的虚拟DOM树,这个新的虚拟DOM会去跟之前旧的虚拟DOM树进行比较差异,并且更新有差异的部分再将新的数据渲染到真实的DOM上面。

key的作用

key可以帮React识别出来哪些元素改变了,比如被添加或者删除。因此应当给一个数组的每一个元素赋予一个确定的标识。

同时一个元素的key最好是这个元素在列表中拥有一个独一无二的字符串。通常可以使用数据中的id作为元素的key,当元素没有id的时候,也可以将index作为key,但是这样会引起一些问题的出现。

在进行新虚拟DOM和旧虚拟DOM的diff比较时,会有以下规则:

1、如果新虚拟DOM从旧虚拟DOM中找到了相同的key:

  • 若新虚拟DOM中的内容没有变,则直接使用旧虚拟DOM中的内容和之前的真实DOM来渲染到页面上

  • 若新虚拟DOM中的内容发生了变化,则使用新虚拟DOM中的内容,创建新的真实DOM,然后替换掉之前的真实DOM来渲染到页面上

2、如果新虚拟DOM中没有找到与旧虚拟DOM中相同的key,则会直接使用新虚拟DOM中的内容来创建新的真实DOM,然后渲染到页面上

为什么遍历列表,key最好不要使用index?

当使用index作为key时可能引发的问题:

(1)若对数据进行了逆序添加,逆序删除等破坏顺序的操作的话,会产生没有必要的真实DOM更新

(2)若结构中还包含了输入类的DOM,会产生错误的DOM更新,导致页面有问题

class App extends React.Component {
    state = {
        numbers: [1, 2, 3, 4, 5]
    }

    addItem = () => {
        this.setState({
            numbers: [this.state.numbers.length + 1, ...this.state.numbers]
        })
    }
    render() {
        const { numbers } = this.state;
        return (
            <>
                <h3>
                    {numbers.map((number, index) => (
                    // 使用index值作为key值
                        <div key={index}>
                            {number}:
                            <input type="text" />
                        </div>
                    ))}
                </h3>
                <button onClick={this.addItem}>Add Item</button>
            </>
        )
    }
}

若使用index作为key值时,对数据进行了逆序的添加删除,此时页面就会出现以下的错误
在这里插入图片描述
在这里插入图片描述

class App extends React.Component {
    state = {
        numbers: [1, 2, 3, 4, 5]
    }

    addItem = () => {
        this.setState({
            numbers: [this.state.numbers.length + 1, ...this.state.numbers]
        })
    }
    render() {
        const { numbers } = this.state;
        return (
            <>
                <h3>
                    {numbers.map((number) => (
                        // 使用唯一的值作为key值
                        <div key={number}>
                            {number}:
                            <input type="text" />
                        </div>
                    ))}
                </h3>
                <button onClick={this.addItem}>Add Item</button>
            </>
        )
    }
}

当使用唯一的值作为key值时,无论是正序还是逆序都不会影响到页面。
在这里插入图片描述
在这里插入图片描述

表单

非受控组件

不需要React来管理,现取现用

class App extends React.Component {

    handleSubmit = (e) => {
        e.preventDefault();
        alert(this.name.value)
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    名字:
                    <input type="text" ref={c => this.name = c} />
                </label>
                <input type="submit" value="提交" />
            </form>
        )
    }
}

受控组件

渲染表单的React组件还控制着用户输入过程中表单发生的操作。被React以这样的方式控制取值的表单输入元素就叫做“受控组件”

class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: ''
        }
    }

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

    handleSubmit = (e) => {
        e.preventDefault();
        alert('提交成功!提交的名字为:' + this.state.value)
    }

    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    名字:
                    <input type="text" value={this.state.value} onChange={this.handleChange} />
                </label>
                <input type="submit" value="提交" />
            </form>
        )
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Maike.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值