React学习1

简介: 最近做一个项目需要用到react,就开始学习一下react吧

1.起步

1.React安装


npm i react react-dom
  <div id="root"></div>
  <!-- 1 引入js文件 -->
  <script src="./node_modules/react/umd/react.development.js"></script>
  <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>
  <script>
    // 2 创建react元素
    // 参数一:元素名称
    // 参数二:元素属性
    // 参数三:元素的子节点
    const title = React.createElement('h1', null, 'Hello React')
    // 3 渲染react元素
    // 参数一:要渲染的react元素
    // 参数二:挂载点
    ReactDOM.render(title, document.getElementById('root'))
  </script>

2.安装脚手架

npx create-react-app my-app

导入react


import React from 'react'

import ReactDOM from 'react-dom'

3.JSX 的基本使用

JSX自动补全html标签



const title = <h1>hello jsx</h1>

注意点



JS表达式

/* 
  JSX中使用JavaScript表达式
*/
const name = 'Jack'
const age = 19
const title = (
  <h1>
    Hello JSX, {name}, 年龄:{age}
  </h1>
)

JSX中使用JavaScript表达式的注意点:


import React from 'react'
import ReactDOM from 'react-dom'
/* 
  JSX中使用JavaScript表达式的注意点:
*/
// 函数调用表达式
const sayHi = () => 'Hi~'
const dv = <div>我是一个div</div>
const title = (
  <h1>
    Hello JSX
    <p>{1}</p>
    <p>{'a'}</p>
    <p>{1 + 7}</p>
    <p>{3 > 5 ? '大于' : '小于等于'}</p>
    <p>{sayHi()}</p>
    {dv}
    {/* 错误演示 */}
    {/* <p>{ {a: '6'} }</p> */}
    {/* { if (true) {} } */}
    {/* { for (var i = 0; i < 10; i++) {} } */}
  </h1>
)
// 渲染react元素
ReactDOM.render(title, document.getElementById('root'))


结果



条件渲染


const isLoading = true
// 逻辑与运算符:
const loadData = () => {
  return isLoading && (<div>loading...</div>)
}
const title = (
  <h1>
    条件渲染:
    {loadData()}
  </h1>
)


const isLoading = true
// if-else
const loadData = () => {
  if (isLoading) {
    return <div>loading...</div>
  }
  return <div>数据加载完成,此处显示加载后的数据</div>
}


const isLoading = false
// 三元表达式:
const loadData = () => {
  return isLoading ? (<div>loading...</div>) : (<div>数据加载完成,此处显示加载后的数据</div>)
}

列表渲染

/* 
  列表渲染:
*/
// 歌曲列表:
const songs = [
  {id: 1, name: '痴心绝对'},
  {id: 2, name: '像我这样的人'},
  {id: 3, name: '南山南'},
]
const list = (
  <ul>
    {songs.map(item => <li key={item.id}>{item.name}</li>)}
  </ul>
)
// 渲染react元素
ReactDOM.render(list, document.getElementById('root'))


css



4.React组件

创建组件的两种方法



函数组件




const Hello=()=> <div>函数组件</div>
// 渲染react元素
ReactDOM.render(<Hello/>, document.getElementById('root'))
类组件



class Hello extends React.Component {
  render() {
    return (
      <div>第一个类组件</div>
    )
  }
}
// 渲染react元素
ReactDOM.render(<Hello/>, document.getElementById('root'))

5.React 事件处理

事件绑定


类组件事件绑定


class App extends React.Component {
  // 事件处理程序
  handleClick() {
    console.log('单击事件触发了')
  }
  render() {
    return (
      <button onClick={this.handleClick}>点我,点我</button>
    )
  }
}

函数组件事件绑定


function App() {
  // 事件处理程序
  function handleClick() {
    console.log('函数组件中的事件绑定,事件触发了')
  }
  return (
    <button onClick={handleClick}>点我</button>
  )
}

事件对象


class App extends React.Component {
  handleClick(e) {
    // 阻止浏览器的默认行为
    e.preventDefault()
    console.log('a标签的单击事件触发了')
  }
  render() {
    return (
      <a href="http://itcast.cn/" onClick={this.handleClick}>zjhhhhh</a>
    )
  }
}

6.有状态组件和无状态组件


state的基本使用



class App extends React.Component {
  /* constructor() {
    super()
    // 初始化state
    this.state = {
      count: 0
    }
  } */
  // (es6)简化语法初始化state(推荐)
  state = {
    count: 10
  }
  render() {
    return (
      <div>
        <h1>计数器:{ this.state.count }</h1>
      </div>
    )
  }
}
setState的基本使用
class App extends React.Component {
  state = {
    count: 0,
    test: 'a'
  }
  render() {
    return (
      <div>
        <h1>计数器:{ this.state.count }</h1>
        <button onClick={() => {
          this.setState({
            count: this.state.count + 1
          })
          // 错误!!!
          // this.state.count += 1
        }}>+1</button>
      </div>
    )
  }
}

7.this指向问题

JSX中掺杂过多JS逻辑代码,会显得非常混乱,所以我们将逻辑抽离到单独的方法中,保证JSX 结构清晰


如下列代码


class App extends React.Component {
  state = {
    count: 0
  }
  // 事件处理程序
  onIncrement() {
    console.log('事件处理程序中的this:', this)
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>计数器:{ this.state.count }</h1>
        <button onClick={this.onIncrement}>+1</button>
        {/* <button onClick={() => {
          this.setState({
            count: this.state.count + 1
          })
        }}>+1</button> */}
      </div>
    )
  }
}


但是运行会报错


原因是事件处理程序中this的值为undefined,我们希望this指向组件实例(render方法的this即为组件实例)


有三种解决方法


1.箭头函数


class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 0
    }
    this.onIncrement = this.onIncrement.bind(this)
  }
  // 事件处理程序
  onIncrement() {
    console.log('事件处理程序中的this:', this)
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>计数器:{ this.state.count }</h1>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}
 2.Function.prototype.bind()
class App extends React.Component {
  constructor() {
    super()
    this.state = {
      count: 0
    }
    this.onIncrement = this.onIncrement.bind(this)
  }
  // 事件处理程序
  onIncrement() {
    console.log('事件处理程序中的this:', this)
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>计数器:{ this.state.count }</h1>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}


3.class的实例方法


class App extends React.Component {
  state = {
    count: 0
  }
  // 事件处理程序
  onIncrement = () => {
    console.log('事件处理程序中的this:', this)
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <h1>计数器:{ this.state.count }</h1>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}


8.表单处理


受控组件
class App extends React.Component {
  state = {
    txt: ''
  }
  handleChange = e => {
    this.setState({
      txt: e.target.value
    })
  }
  render() {
    return (
      <div>
        <input type="text" value={this.state.txt} onChange={this.handleChange} />
      </div>
    )
  }
}



class App extends React.Component {
  state = {
    txt: '',
    content: '',
    city: 'bj',
    isChecked: false
  }
  handleChange = e => {
    this.setState({
      txt: e.target.value
    })
  }
  // 处理富文本框的变化
  handleContent = e => {
    this.setState({
      content: e.target.value
    })
  }
  // 处理下拉框的变化
  handleCity = e => {
    this.setState({
      city: e.target.value
    })
  }
  // 处理复选框的变化
  handleChecked = e => {
    this.setState({
      isChecked: e.target.checked
    })
  }
  render() {
    return (
      <div>
        {/* 文本框 */}
        <input type="text" value={this.state.txt} onChange={this.handleChange} />
        <br/>
        {/* 富文本框 */}
        <textarea value={this.state.content} onChange={this.handleContent}></textarea>
        <br/>
        {/* 下拉框 */}
        <select value={this.state.city} onChange={this.handleCity}>
          <option value="sh">上海</option>
          <option value="bj">北京</option>
          <option value="gz">广州</option>
        </select>
        <br/>
        {/* 复选框 */}
        <input type="checkbox" checked={this.state.isChecked} onChange={this.handleChecked} />
      </div>
    )
  }
}

多表单元素的优化



class App extends React.Component {
  state = {
    txt: '',
    content: '',
    city: 'bj',
    isChecked: false
  }
  handleForm = e => {
    // 获取当前DOM对象
    const target = e.target
    // 根据类型获取值
    const value = target.type === 'checkbox'
      ? target.checked
      : target.value
    // 获取name
    const name = target.name
    this.setState({
      [name]: value
    })
  }
  render() {
    return (
      <div>
        {/* 文本框 */}
        <input type="text" name="txt" value={this.state.txt} onChange={this.handleForm} />
        <br/>
        {/* 富文本框 */}
        <textarea name="content" value={this.state.content} onChange={this.handleForm}></textarea>
        <br/>
        {/* 下拉框 */}
        <select name="city" value={this.state.city} onChange={this.handleForm}>
          <option value="sh">上海</option>
          <option value="bj">北京</option>
          <option value="gz">广州</option>
        </select>
        <br/>
        {/* 复选框 */}
        <input type="checkbox" name="isChecked" checked={this.state.isChecked} onChange={this.handleForm} />
      </div>
    )
  }
}

非受控组件



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>
    )
  }
}


9. props

const Hello = props => {
  console.log('props:', props)
  props.fn()
  return (
    <div>
      <h1>props:</h1>
      {props.tag}  
    </div>
  )
}
ReactDOM.render(
  <Hello
    name="rose"
    age={19}
    colors={['red', 'green', 'blue']}
    fn={() => console.log('这是一个函数')}
    tag={<p>这是一个p标签</p>}
  />,
  document.getElementById('root')
)


组件通讯3种方式



子传父


class Parent extends React.Component {
  state = {
    parentMsg: ''
  }
  // 提供回调函数,用来接收数据
  getChildMsg = data => {
    console.log('接收到子组件中传递过来的数据:', data)
    this.setState({
      parentMsg: data
    })
  }
  render() {
    return (
      <div className="parent">
        父组件:{this.state.parentMsg}
        <Child getMsg={this.getChildMsg} />
      </div>
    )
  }
}
// 子组件
class Child extends React.Component {
  state = {
    msg: '刷抖音'
  }
  handleClick = () => {
    // 子组件调用父组件中传递过来的回调函数
    this.props.getMsg(this.state.msg)
  }
  render() {
    return (
      <div className="child">
        子组件:{' '}
        <button onClick={this.handleClick}>点我,给父组件传递数据</button>
      </div>
    )
  }
}
ReactDOM.render(<Parent />, document.getElementById('root'))


兄弟组件


// 父组件
class Counter extends React.Component {
  // 提供共享状态
  state = {
    count: 0
  }
  // 提供修改状态的方法
  onIncrement = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <Child1 count={this.state.count} />
        <Child2 onIncrement={this.onIncrement} />
      </div>
    )
  }
}
const Child1 = props => {
  return <h1>计数器:{props.count}</h1>
}
const Child2 = props => {
  return <button onClick={() => props.onIncrement()}>+1</button>
}
ReactDOM.render(<Counter />, document.getElementById('root'))



props校验


安装prop-types


npm i prop-types

props默认值


10.Context




步骤


(1) 创建context得到两个组件


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

(2)用Provider组件包裹根组件(value代表要传的值)

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

(3)目标子组件包裹要Consumer组件 (data就是传过来的值, {data}在标签中渲染data)

const Child = props => {
  return (
    <div className="child">
      <Consumer>{data => <span>我是子节点 -- {data}</span>}</Consumer>
    </div>
  )
}


11.组件的生命周期



创建时


更新时


setState和forceUpdate()


class App extends React.Component {
  constructor(props) {
    super(props)
    // 初始化state
    this.state = {
      count: 0
    }
  }
  // 打豆豆
  handleClick = () => {
     this.setState({
       count: this.state.count + 1
     })  }
  render() {
    console.warn('生命周期钩子函数: render')
    return (
      <div>
        <Counter count={this.state.count} />
        <button onClick={this.handleClick}>打豆豆</button>
      </div>
    )
  }
}
class Counter extends React.Component {
  render() {
    console.warn('--子组件--生命周期钩子函数: render')
    return <h1>统计豆豆被打的次数:{this.props.count}</h1>
  }
}


强制更新


// 演示强制更新:
this.forceUpdate()
class Counter extends React.Component {
  render() {
    console.warn('--子组件--生命周期钩子函数: render')
    return <h1 id="title">统计豆豆被打的次数:{this.props.count}</h1>
  }
  // 注意:如果要调用 setState() 更新状态,必须要放在一个 if 条件中
  // 因为:如果直接调用 setState() 更新状态,也会导致递归更新!!!
  componentDidUpdate(prevProps) {
    console.warn('--子组件--生命周期钩子函数: componentDidUpdate')
    // 正确做法:
    // 做法:比较更新前后的props是否相同,来决定是否重新渲染组件
    console.log('上一次的props:', prevProps, ', 当前的props:', this.props)
    if (prevProps.count !== this.props.count) {
       this.setState({})
      // 发送ajax请求的代码
    }
    // 错误演示!!!
    // this.setState({})
    // 获取DOM
    // const title = document.getElementById('title')
    // console.log(title.innerHTML)
  }
}



卸载时


class Counter extends React.Component {
  componentDidMount() {
    // 开启定时器
    this.timerId = setInterval(() => {
      console.log('定时器正在执行~')
    }, 500)
  }
  render() {
    return <h1>统计豆豆被打的次数:{this.props.count}</h1>
  }
  componentWillUnmount() {
    console.warn('生命周期钩子函数: componentWillUnmount')
    // 清理定时器
    clearInterval(this.timerId)
  }
}


12.render-props和高阶组件

render props模式

class Mouse extends React.Component {
  // 鼠标位置state
  state = {
    x: 0,
    y: 0
  }
  // 鼠标移动事件的事件处理程序
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }
  // 监听鼠标移动事件
  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  render() {
    // return null
    return this.props.render(this.state)
  }
}
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>render props 模式</h1>
        <Mouse
          render={mouse => {
            return (
              <p>
                鼠标位置:{mouse.x} {mouse.y}
              </p>
            )
          }}
        />
      </div>
    )
  }
}


把鼠标变成一只猫


  <Mouse
          render={mouse => {
            return (
              <img
                src={img}
                alt="猫"
                style={{
                  position: 'absolute',
                  top: mouse.y - 64,
                  left: mouse.x - 64
                }}
              />
            )
          }}
        />
      </div>
    )
  }
}

更推荐用children属性代替render属性,即在Mouse组件嵌套一个子节点

<Mouse>
          {mouse => (
            <img
              src={img}
              alt="猫"
              style={{
                position: 'absolute',
                top: mouse.y - 64,
                left: mouse.x - 64
              }}
            />
          )}
        </Mouse>

组件中的render函数也要改变


render() {
    return this.props.children(this.state)
  }

代码优化



高阶组件



创建高阶组件
// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 控制鼠标状态的逻辑
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    render() {
      return <WrappedComponent {...this.state} />
    }
  }
  return Mouse
}


包装组件


const Position = props => (
  <p>
    鼠标当前位置:(x: {props.x}, y: {props.y})
  </p>
)
// 猫捉老鼠的组件:
const Cat = props => (
  <img
    src={img}
    alt=""
    style={{
      position: 'absolute',
      top: props.y - 64,
      left: props.x - 64
    }}
  />
)
// 获取增强后的组件:
const MousePosition = withMouse(Position)
// 调用高阶组件来增强猫捉老鼠的组件:
const MouseCat = withMouse(Cat)
 渲染包装后的组件
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>高阶组件</h1>
        {/* 渲染增强后的组件 */}
        <MousePosition />
        <MouseCat />
      </div>
    )
  }
}
displayName 
// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    // 鼠标状态
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    // 控制鼠标状态的逻辑
    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    componentWillUnmount() {
      window.removeEventListener('mousemove', this.handleMouseMove)
    }
    render() {
      return <WrappedComponent {...this.state} />
    }
  }
  // 设置displayName
  Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
  return Mouse
}
function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}


传递props


const MousePosition = withMouse(Position)

往被包装的组件传递props,这时被包装的组件拿不到这个props


<MousePosition a="1" />

需要高阶组件传给他

render() {
      console.log('Mouse:', this.props)
      return <WrappedComponent {...this.state} {...this.props} />
    }


相关文章
|
2月前
|
前端开发 JavaScript
React学习之——条件渲染
【10月更文挑战第16天】React 中没有像Vue中v-if这种指令。React 中的条件渲染和 JavaScript 中的一样,使用 JavaScript 运算符 if 或者条件运算符去创建元素来表现当前的状态,然后让 React 根据它们来更新 UI。
|
3月前
|
前端开发 JavaScript
学习react基础(3)_setState、state、jsx、使用ref的几种形式
本文探讨了React中this.setState和this.state的区别,以及React的核心概念,包括核心库的使用、JSX语法、类与函数组件的区别、事件处理和ref的使用。
74 3
学习react基础(3)_setState、state、jsx、使用ref的几种形式
|
3月前
|
前端开发 JavaScript
react学习(13)props
react学习(13)props
|
3月前
|
前端开发
学习react基础(2)_props、state和style的使用
本文介绍了React中组件间数据传递的方式,包括props和state的使用,以及如何在React组件中使用style样式。
33 0
|
28天前
|
前端开发 JavaScript 安全
学习如何为 React 组件编写测试:
学习如何为 React 组件编写测试:
37 2
|
2月前
|
资源调度 前端开发 JavaScript
React进阶学习
React进阶学习
15 1
|
2月前
|
JSON 前端开发 JavaScript
React 进阶阶段学习计划
React 进阶阶段学习计划
|
3月前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
31 3
|
3月前
|
前端开发
react学习(17)回调形式的ref
react学习(17)回调形式的ref
|
3月前
|
前端开发
react学习(15)函数式组件中使用props
react学习(15)函数式组件中使用props