react笔记(一)

一.核心概念

//npx 是 npm 5.2+ 附带的 package 运行工具。
npx create-react-app my-app
cd my-app
npm start

1.1 表达式

通过{}在jsx中写表达式

  • 变量
  • 函数
  • jsx
实例

修改my-app/src/index.js文件

// 变量
import React from 'react'
import ReactDOM from 'react-dom'

const name = 'zs'
const element = <h1>Hello, {name}</h1>  //使用表达式

ReactDOM.render(
  element,
  document.getElementById('root')
)

在这里插入图片描述

// 函数
import React from 'react'
import ReactDOM from 'react-dom'

function formatName(name){
  return name.firstName + ' ' + name.lastName
}
const name = {
  firstName: 'sss',
  lastName: 'zzz'
}
const element = <h1>Hello, {formatName(name)}</h1>  //使用表达式

ReactDOM.render(
  element,
  document.getElementById('root')
)

在这里插入图片描述

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

function formatName(name){
  return <h1>Hello, {name.firstName + ' ' + name.lastName}</h1>  
}
const name = {
  firstName: 'sss',
  lastName: 'zzz'
}
const element = <div>{formatName(name)}</div> //使用表达式

ReactDOM.render(
  element,
  document.getElementById('root')
)

element中的div包裹formatName函数返回的jsx代码:
在这里插入图片描述

1.2 元素渲染

元素是构成 React 应用的最小砖块,组件由元素构成

React元素是不可变对象。一旦被创建,就无法更改它的子元素或属性。更新React元素的唯一方式就是创建一个全新的元素,并将其传入ReactDOM.render()

修改my-app/src/index.js文件

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

const element = <div>今天是:{new Date().toTimeString()}.</div>
ReactDOM.render(
  element,
  document.getElementById('root')
)  

虽然日期一直在变化,但是由于React元素不可变,页面不会更新
在这里插入图片描述
通过**创建新的元素,传入ReactDOM.render()**来实现页面的动态更新,可以看到页面的时间每秒动态变化

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

setInterval(() => {
  const element = <div>今天是:{new Date().toTimeString()}.</div>
  ReactDOM.render(
    element,
    document.getElementById('root')
  )  
  
}, 1000)
1.2.1 条件渲染
import React from 'react';
import ReactDOM from 'react-dom';

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

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

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (isLoggedIn) {
    return <UserGreeting />;
  }
  return <GuestGreeting />;
}

ReactDOM.render(
  // Try changing to isLoggedIn={true}:
  <Greeting isLoggedIn={true} />,
  document.getElementById('root')
);
1.2.3 阻止组件渲染

render 方法直接返回 null,就不进行任何渲染

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

function Greeting(props) {
  const isLoggedIn = props.isLoggedIn;
  if (!isLoggedIn) {
    return null // 当 props.isLoggedIn 为false时,不渲染组件
  }
  return <h1>hello</h1>;
}

ReactDOM.render(
  <Greeting isLoggedIn={false} />,
  document.getElementById('root')
);

1.3 组件

1.3.1 函数组件(组件名称必须以大写字母开头)
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
  • 接收参数

参数props获取所有传递给该组件的参数
在这里插入图片描述

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

// 自定义组件
function Welcome(props) {
  console.log(props) //{name: "zs", age: "12", sex: "female"}
  return <h1>Hello, {props.name}</h1>;
}
// 向自定义组件传递参数
const element = <Welcome name="zs" age="12" sex="female"></Welcome>

ReactDOM.render(
  element,
  document.getElementById('root')
)
  • 渲染

直接返回jsx代码

1.3.2 class组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
  • 接受参数

使用this.props获取所有传递给该组件的参数

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

// 自定义组件
class Welcome extends React.Component {
  render() {
    console.log(this.props) //{name: "zs", age: "12", sex: "female"}
    return <h1>Hello, {this.props.name}</h1>;
  }
}
// 向自定义组件传递参数
const element = <Welcome name="zs" age="12" sex="female"></Welcome>

ReactDOM.render(
  element,
  document.getElementById('root')
)
  • 渲染

通过rander函数返回jsx代码

1.4 生命周期

  • 组件第一次被渲染到DOM中componentDidMount

  • 组件被删除componentWillUnmount

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


class Clock extends React.Component{
  constructor(props){ // 构造函数
    super(props)
    // 局部的state
    this.state = {
      date: new Date()
    }
  }
  // 组件第一次被渲染到DOM中
  componentDidMount(){
    this.timeId = setInterval(() => {
      //调用方法
      this.tick()
    }, 1000)
  }
  // 组件被删除的时候
  componentWillUnmount(){
    clearInterval(this.timeId)
  }
  //定义方法
  tick(){
    this.setState({
      date: new Date()
    })
  }
  render(){
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    )
  } 
}


ReactDOM.render(
  <Clock/>,
  document.getElementById('root')
);
修改state,更新会被合并
  • 接收对象
this.setState({comment: 'Hello'});
  • 接收函数

this.propsthis.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});
// Correct
this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

1.5 事件处理

绑定事件
import React from 'react';
import ReactDOM from 'react-dom';


class Link extends React.Component{
  //定义方法
  handleClick(e){
    // 阻止默认事件
    e.preventDefault()
    console.log('the link is clicked')
  }
  render(){
    return (
      <a href = "#" onClick={this.handleClick}>
        Click me
      </a>
    )
  } 
}

ReactDOM.render(
  <Link/>,
  document.getElementById('root')
);
事件中this

下面的写法没有绑定this,调用该方法时的this输出为undefined

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

class Link extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      isTroggleOn:true
    }
  }
  //定义方法
  handleClick(){
    console.log(this)  //输出undefined
    this.setState((state) => 
      ({isTroggleOn: !state.isTroggleOn})
    )
  }
  render(){
    return (
      <button onClick={this.handleClick}>开关</button>
    )
  } 
}

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

有3种修改方法

  • 第一种

在构造函数中为该方法绑定this

  constructor(props){
    super(props)
    this.state = {
      isTroggleOn:true
    }
    // 在回调中使用this,需要先绑定
    this.handleClick = this.handleClick.bind(this)
  }
  • 第二种

定义方法的时候使用箭头函数,箭头函数中的this指向定义箭头函数时它所在的上下文

  //定义方法
  handleClick = () => { 
    this.setState((state) => 
      ({isTroggleOn: !state.isTroggleOn})
    )
  }
  • 第三种

使用箭头函数调用函数

问题在于每次渲染该组件(Link)的时候都会创建不同的回调函数

多数情况下没什么问题,如果该回调函数作为prop传入子组件时,这些组件可能会进行额外的渲染

  render(){
    return (
      <button onClick={() => {this.handleClick()}}>开关</button>
    )
  } 

如果需要传参,可以使用下面的2种写法:

  • 第一种
class Link extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      isTroggleOn:true
    }
  }
  //定义方法
  handleClick(id){
    console.log(this) //Link {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …} 
    console.log(id) // 12
    this.setState((state) => 
      ({isTroggleOn: !state.isTroggleOn})
    )
  }
  render(){
    return (
      <button onClick={this.handleClick.bind(this, '12')}>开关</button>
    )
  } 
}
  • 第二种
  render(){
    return (
      <button onClick={(e) => this.handleClick('12', e)}>开关</button>
    )
  }

1.6 列表

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

const numbers = [1,2,3,4,5]
const listItems = numbers.map((number) => 
  <li> {number} </li>
)

ReactDOM.render(
  <ul> {listItems} </ul>,
  document.getElementById('root')
);

会看到一个警告 a key should be provided for list items,意思是当你创建一个元素时,必须包括一个特殊的 key 属性

通过在li标签上添加key属性消除该警告

const listItems = numbers.map((number) => 
  <li key = {number.toString()}> {number} </li>
)

1.7 表单

1.7.1 from表单
import React from 'react';
import ReactDOM from 'react-dom';

// !!! 组件名称开头必须为大写字母,欧泽会报错
class MyFrom extends React.Component{
  constructor(props){
    super(props)
    this.state = {
      value:''
    }
    // 绑定this,否则方法中的this为undefined,产生报错
    // 单纯写this.handleChange.bind(this)不能绑定this
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }
  handleChange(e){
    this.setState({
      value:e.target.value
    })
  }
  handleSubmit(e){
    alert('提交的值:' + this.state.value)
    e.preventDefault()
  }
  render(){
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text" value={this.state.value} onChange={this.handleChange} />
        <input type="submit" value="submit" />
      </form>
    )
  }
}

ReactDOM.render(
  <MyFrom/>,
  document.getElementById('root')
);
1.7.2 textarea

使用value属性绑定textarea内的值

<textarea type="text" value={this.state.value} onChange={this.handleChange} />
1.7.3 select标签

在select标签上使用value属性绑定选中的值

      // defaultValue指定表单的默认值,value绑定表单选中的值
      // defaultValue和value只能使用其中1个
      <div>
        <select value={this.state.value} onChange={this.handleChange}>
          <option value="grapefruit">葡萄柚</option>
          <option value="lime">酸橙</option>
          <option value="coconut">椰子</option>
          <option value="mango">芒果</option>
        </select>
        <button onClick={this.handleSubmit}>提交</button>
      </div>
1.7.4 input

需要处理多个输入时,可以给每个元素添加name属性,处理函数根据event.target.name选择要执行的操作

  render(){
    return (
      <div>
        <input name="input1" type="text" value={this.state.value1} onChange={this.handleChange} />
        <input name="input2" type="text" value={this.state.value2} onChange={this.handleChange} />
      </div>
    )
  }

1.8 状态提升

父组件通过属性绑定数据向子组件传递数据:

<TemperatureInput 
	scale="c"
	temperature={celsius}
	onTemperatureChange={this.handleCelsiusChange}/>

子组件通过调用父组件传递过来的方法来该边父组件中的数据:

<input
	value={temperature}
	onChange={this.handleChange} />
// 其中handleChange方法直接调用父元素中的方法
  handleChange(e){
    this.props.onTemperatureChange(e.target.value)
  }

先编写各个功能的组件:

  1. 判断当前温度会不会沸腾的组件
// 1.判断当前温度会不会沸腾的组件
function BoilingVerdict(props){
  // 1.1 高于100摄氏度,沸腾
  if(props.temperature >= 100){
    return <p>The Water would boil.</p>
  }
  // 1.2 低于100摄氏度,不会沸腾
  return <p>The Water would not boil.</p>
}
  1. 摄氏度温度显示框组件、华氏度温度显示框组件
// 2.1摄氏度温度显示框
class CelsiusTemperatureInput extends React.Component{
  constructor(props){
    super(props)
    this.state={
      // 温度
      temperature:''
    }
    this.handleChange = this.handleChange.bind(this)
  }
  // 更新温度
  handleChange(e){
    this.setState({
      temperature:e.target.value
    })
  }
  render(){
    const temperature = this.state.temperature
    return(
      <fieldset>
        <legend>Enter temperature in Celsius:</legend>
        <input
          value={temperature}
          onChange={this.handleChange} />
      </fieldset>      
    )
  }
}
// 2.2华氏度温度显示框
class FahrenheitTemperatureInput extends React.Component{
  constructor(props){
    super(props)
    this.state={
      // 温度
      temperature:''
    }
    this.handleChange = this.handleChange.bind(this)
  }
  // 更新温度
  handleChange(e){
    this.setState({
      temperature:e.target.value
    })
  }
  render(){
    const temperature = this.state.temperature
    return(
      <fieldset>
        <legend>Enter temperature in Fahrenheit:</legend>
        <input
          value={temperature}
          onChange={this.handleChange} />
      </fieldset>      
    )
  }
}

渲染到界面,可以看到下面的效果:

ReactDOM.render(
  <div>
    <CelsiusTemperatureInput/>
    <FahrenheitTemperatureInput/>
  </div>,
  document.getElementById('root')
);

在这里插入图片描述
可以看到这两个组件只有标题和数据不同,因此可以合并为同一个组件:

标题和数据通过属性传递进去:

// 2.摄氏度温度显示框
class TemperatureInput extends React.Component{
  constructor(props){
    super(props)
    this.handleChange = this.handleChange.bind(this)
  }
  render(){
    const temperature = this.props.temperature
    const scale = this.props.scale
    return(
      <fieldset>
        <legend>Enter temperature in {scale}:</legend>
        <input
          value={temperature} />
      </fieldset>      
    )
  }
}
ReactDOM.render(
  <div>
    <TemperatureInput temperature="10" scale="Celsius"/>
    <TemperatureInput temperature="50" scale="Fahrenheit"/>
  </div>,
  document.getElementById('root')
);
  1. 定义一个组件管理上面的组件
// 3. 定义一个组件管理上面的组件
class Calculator extends React.Component{
  constructor(props){
    super(props)
    this.state={

    }
  }
  render(){
    return(
      <div>
        {/* 摄氏度组件 */}
        <TemperatureInput temperature="10" scale="Celsius"/>
        {/* 华氏度组件 */}
        <TemperatureInput temperature="50" scale="Fahrenheit"/>
        {/* 显示是否沸腾 */}
        <BoilingVerdict temperature="10"/>
      </div>
    )
  }
}

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

此时传入的摄氏度温度和华氏度温度是固定的,如果想让他可变,就把他变成管理组件的一个状态

此时传入的温度类型也是固定的,如果想让他可变,就把他变成管理组件的一个状态

先准备摄氏度转化为华氏度,华氏度转化为摄氏度的方法:

//华氏度转化为摄氏度
function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}
//摄氏度转化为华氏度
function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}
//传入温度temperature和转化函数convert
function tryConvert(temperature, convert){
  const input = parseFloat(temperature) //温度
  if(Number.isNaN(input)){
    return ''
  }
  const output = convert(input) //转化温度
  const rounded = Math.round(output * 1000) / 1000
  return rounded.toString()
}
class Calculator extends React.Component{
  constructor(props){
    super(props)
    this.state={
      temperature:'10', //温度
      scale:'Celsius' //温度类型,默认是摄氏度
    }
  }
  render(){
    const scale = this.state.scale
    const temperature = this.state.temperature
    // 摄氏度
    const celsius = scale === 'Fahrenheit' ? tryConvert(temperature, toCelsius) : temperature
    // 华氏度
    const fahrenheit = scale === 'Celsius' ? tryConvert(temperature, toFahrenheit) : temperature
    return(
      <div>
        {/* 摄氏度组件 */}
        <TemperatureInput temperature={celsius} scale={scale}/>
        {/* 华氏度组件 */}
        <TemperatureInput temperature={fahrenheit} scale={scale}/>
        {/* 显示是否沸腾 */}
        <BoilingVerdict temperature={celsius}/>
      </div>
    )
  }
}

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

此时管理组件的温度是固定的,可以通过温度组件修改温度来更改管理组件中的数据

// 2.摄氏度温度显示框
class TemperatureInput extends React.Component{
  constructor(props){
    super(props)
    this.handleChange = this.handleChange.bind(this)
  }
  // 更新温度
  handleChange(e){
    //调用父组件利用onTemperatureChange参数传递古来的方法
    this.props.onTemperatureChange(e.target.value)
  }
  render(){
    const temperature = this.props.temperature
    const scale = this.props.scale
    return(
      <fieldset>
        <legend>Enter temperature in {scale}:</legend>
        <input
          value={temperature}
          onChange={this.handleChange} />
      </fieldset>      
    )
  }
}

// 3. 定义一个组件管理上面的组件
class Calculator extends React.Component{
  constructor(props){
    super(props)
    this.state={
      temperature:'10', //温度
      scale:'Celsius' //温度类型,默认是摄氏度
    }
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this)
    this.handleFahrenheitChang = this.handleFahrenheitChang.bind(this)
  }
  handleCelsiusChange(temperature){
    this.setState({
      scale:'Celsius',temperature:temperature
    })
  }
  handleFahrenheitChang(temperature){
    this.setState({
      scale:'Fahrenheit',temperature:temperature
    })
  }
  render(){
    const scale = this.state.scale
    const temperature = this.state.temperature
    // 摄氏度
    const celsius = scale === 'Fahrenheit' ? tryConvert(temperature, toCelsius) : temperature
    // 华氏度
    const fahrenheit = scale === 'Celsius' ? tryConvert(temperature, toFahrenheit) : temperature
    return(
      <div>
        {/* 摄氏度组件 */}
        <TemperatureInput temperature={celsius} scale={scale} onTemperatureChange={this.handleCelsiusChange}/>
        {/* 华氏度组件 */}
        <TemperatureInput temperature={fahrenheit} scale={scale} onTemperatureChange={this.handleFahrenheitChang}/>
        {/* 显示是否沸腾 */}
        <BoilingVerdict temperature={celsius}/>
      </div>
    )
  }
}

在这里插入图片描述
4. 最终代码:

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

const scaleNames = {
  c:'Celsius',
  f:'Fahrenheit'
}

//转换函数
function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

function tryConvert(temperature, convert){
  const input = parseFloat(temperature) //温度
  if(Number.isNaN(input)){
    return ''
  }
  const output = convert(input) //转化温度
  const rounded = Math.round(output * 1000) / 1000
  return rounded.toString()
}

function BoilingVerdict(props){
  if(props.celsius >= 100){
    return <p>The Water would boil.</p>
  }
  return <p>The Water would not boil.</p>
}

class TemperatureInput  extends React.Component{
  constructor(props){
    super(props)
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e){
    this.props.onTemperatureChange(e.target.value)
  }
  render() {
    const temperature = this.props.temperature
    const scale = this.props.scale
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input
          value={temperature}
          onChange={this.handleChange} />
      </fieldset>
    );
  }
}

class Calculator extends React.Component{
  constructor(props){
    super(props)
    this.state={
      temperature:'',
      scale:'c'
    }
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this)
    this.handleFahrenheitChang = this.handleFahrenheitChang.bind(this)
  }
  handleCelsiusChange(temperature){
    this.setState({
      scale:'c',temperature:temperature
    })
  }
  handleFahrenheitChang(temperature){
    this.setState({
      scale:'f',temperature:temperature
    })
  }
  render(){
    const scale = this.state.scale
    const temperature = this.state.temperature
    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature
    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature
    return (
      <div>
        <TemperatureInput 
          scale="c"
          temperature={celsius}
          onTemperatureChange={this.handleCelsiusChange}/>
        <TemperatureInput 
          scale="f"
          temperature={fahrenheit}
          onTemperatureChange={this.handleFahrenheitChang}/>
        <BoilingVerdict
          celsius={parseFloat(temperature)} />
      </div>
    )
  }
}

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

效果:
在这里插入图片描述

1.9 包含关系(插槽)

props.children指代的是下图中指示的内容
在这里插入图片描述

也可以自行约定需要插入的内容,通过属性传入变量的形式传入内容
在这里插入图片描述

概念

props只读

组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props.

单项数据流

数据是向下流动的。这通常会被叫做“自上而下”或是“单向”的数据流。任何的 state 总是所属于特定的组件,而且从该 state 派生的任何数据或 UI 只能影响树中“低于”它们的组件。

列表中的key

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

key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key

性能

页面更新

React DOM 会将元素和它的子元素与它们之前的状态进行比较,只更新需要更新的部分

安全

jsx防止注入攻击

React DOM 在渲染所有输入内容之前,默认会进行转义。所有的内容在渲染之前都被转换成了字符串,可以有效防止XSS攻击

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值