初识react

npx create-react-app "项目名"

react用到的第三方包

  • classnames
  • styled-components 将css独立来写的第三方包
  • prop-types:props的类型检测工具
  • axios

在jsx里面写js代码就加一个{}

创建一个简单的react:

import React from "react"
import ReactDOM from "react-dom"

// const app = <h1>welcome first!</h1>// jsx语法,不需要加引号
const createApp = (props) => {
    return (
        <div>
            {/* {只要在jsx里面写js代码就要加一层花括号注释也要用花括号} */}
            <h1>欢迎来到{props.title}</h1>
            <p>优秀的{props.title}</p>
        </div>
    )
}
const app = createApp({
    title: '德莱联盟'
})
ReactDOM.render(
    app,
    document.querySelector("#root")
)

react定义组件的方式:

1、箭头函数,组件的首字母大写
2、使用类组件,可以嵌套
<1> 箭头函数方法:

// react创建组件的第一个方式:箭头函数,这个名字要大写 App
const App = (props) => {
    return (
        <div>
            <h1 title={props.title}>想吃很多{props.title}</h1>
            <p>有很多{props.title}</p>
        </div>
    )
}
ReactDOM.render(
    <App title="炸鸡腿"/>,
    document.querySelector("#root")
)

<2>使用类继承React.Component:

// 定义组件的第二个方法:使用类继承React.Component
import React, { Component } from "react"
import { render } from "react-dom"

class App extends Component {
    render () {
        console.log(this.props) //{title: "类组件是继承React.Component的"}  参数传递就用this.props
        return (
            <div>
                <h1>类组件</h1>
                <p>{this.props.title}</p>
            </div>
        )
    }
}
// render 是ReactDOM提供的方法,这个方法通常只会使用一次
render(
    <App title="类组件是继承React.Component的"/>,
    document.querySelector("#root")
)

// 16版本以前创建组件的方法
// React.createClass({
//     render () {
//         return (
//             <div>{this.props.title}</div>
//         )
//     }
// })

组件嵌套

import React, { Component } from "react"
import { render } from "react-dom"

const Header = () => <h1>组件嵌套</h1>
class App extends Component {
    render () {
        return (
            <div>
                <Header />
                <p>{this.props.title}</p>
            </div>
        )
    }
}

render(
    <App title="react组件可以嵌套"/>,
    document.querySelector("#root")
)

jsx原理,通过React.createElement的方式创建元素,有无限个参数,但前两个是固定的,第一个标签名,第二个是标签的属性,剩下的都是子元素
React.createElement(type,[props],[...children])

import React, { Component } from "react"
import { render } from "react-dom"
class App extends Component {
    render () {
        return (
            React.createElement (
                'div',
                {
                    className: 'app',
                    id: 'appRoot'
                },
                React.createElement (
                    'h1',
                    {
                        className: 'title'
                    },
                    'jsx原理'
                ),
                React.createElement (
                    'p',
                    null,
                    'jax到底怎么写?'
                )
            )
        )
    }
}

// const appVDom = {
//     tag: "div",
//     attrs: {
//         className: "app",
//         id: "appRoot"
//     },
//     children: [
//         {
//             tag: "h1",
//             attrs: {
//                 className: "app"
//             },
//             children: [
//                 "jsx原理"
//             ]
//         }, {
//             tag: "p",
//             attrs: null,
//             children: [
//                 "jsx到底怎么写呢?"
//             ]
//         }
//     ]

// }

render(
    <App />,
    document.querySelector("#root")
)

react里的css

1、使用style标签内联创建

import React, { Component } from "react"
import { render } from "react-dom"
import classNames from "classnames"
import styled from "styled-components"
import './index.css'

const Title = styled.h2`
    color: #f00
    `

class App extends Component {
    render () {
        const style = {color: '#f00'}
        return (
            <div>
                <h1>元素的样式</h1>
                <Title>styled-components的使用</Title>
                <ol>
                    <li style={style}>使用style方式</li>
                    <li className='redclass'>使用class方式,但在react里要写成className</li>
                    <li className={classNames('a',{'b': true,'c': false})}>要动态添加className使用classnames第三方包</li>// 该li的class只有a,b没有c
                </ol>
            </div>
        )
    }
}

render(
    <App />,
    document.querySelector("#root")
)

2、使用class方式,但是要写成className,样式写在css文件中,所有要先import引入
第三方的css包:1、classnames,可以动态添加不同的className

3、styled-components 将css独立来写的第三方包

React项目组件化

快捷键:rcc react的class component;rfc react的function component

  1. 整个单页应用的入口文件:App.js

  2. 入口文件:index.js

  3. 其余的子组件包在App下面

  4. 在src下新建一个components目录,里面包含所有的子组件,一个子组件就是一个文件夹,用组件名作为文件夹名字;同时,在components的根目录下还有一个index.js文件,用来引入和导出所有的子组件,在App.js里面引入所有的组件。


    image.png
  5. 在src下创建一个services文件夹,里面有apis.js和index.js文件。apis.js是统一管理接口的文件

组件化开发React todolist, 项目开发中的组件的基本目录结构基本上是这样的:
> /your-project
>
> - src
>   - …
>   - components
>     - YourComponentOne
>       - index.js/YourComponentOne.js
>     - YourComponentTwo
>       - index.js/YourComponentTwo.js
>     - index.js 用于导出组件
注意:一个组件只干一件事情 ,所以TodoList和TodoItem要做成两个组件,这样也方便于后期理解shouldComponentUpdate

一个组件里的return只能有一个根元素 ,因此react里可以使用Fragment(需要import引入,是一个空标签),或者直接使用空标签

import React, { Component, Fragment } from 'react'
import {
    TodoHeader,
    TodoInput,
    TodoList
} from './components'
export default class App extends Component {
  render() {
    return (
      <Fragment>
        <TodoHeader />
        <TodoInput />
        <TodoList />
      </Fragment>

    //   <>
    //     <TodoHeader />
    //     <TodoInput />
    //     <TodoList />
    //   </>
    )
  }
}

两种导出方式:

//第一种,当需要对这里引入的组件进行处理再导出时使用
// import TodoHeader from './TodoHeader'
// import TodoInput from './TodoInput'
// import TodoList from './TodoList'

// export {
//     TodoHeader,
//     TodoInput,
//     TodoList
// }

//第二种
export { default as TodoHeader} from './TodoHeader'
export { default as TodoInput} from './TodoInput'
export { default as TodoList} from './TodoList'

组件的数据挂载方式

1. 通过props传递
function组件直接通过props.xxx,class组件通过this.props.xxx传递

import React from 'react'

export default function TodoHeader(props) {
  console.log(props)// {title: "待办事项"}
  return (
    <h1>
      {props.title}
    </h1>
  )
}

属性(props)

props.children 组件标签内的内容

// App.js
export default class App extends Component {
  render() {
    return (
      <Fragment>
        <TodoHeader desc="完成今天所有的事">
          待办事项列表
        </TodoHeader>
        <TodoInput btnText="ADD"/>
        <TodoList />
      </Fragment>

// TodoHeader/index.js
export default function TodoHeader(props) {
  console.log(props) // {desc: "完成今天所有的事", children: "待办事项列表"}
  return (
    <h1>
      {props.children}
    </h1>
  )
}

prop-types:props的类型检测工具

// TodoHeader/index.js
import React from 'react'
import PropTypes from 'prop-types'
export default function TodoHeader(props) {
  console.log(props)
  return (
    <>
      <h1>
        {props.children}
      </h1>
      <h3>{props.desc}</h3>
      <p>{props.x + props.y}</p>
    </>
  )
}
TodoHeader.propTypes = {
  desc: PropTypes.string,
  x: PropTypes.number.isRequired,
  y: PropTypes.number.isRequired
}

class组件写法:

// TodoInput.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
  static propTypes = {
    btnText: PropTypes.string
  }
  static defaultProps = {
    btnText: '添加TODO' //组件默认值
  }
//TodoHeader.defaultProps = {
//  desc: '如果能重来'
//}  //funciton组件写法
  render() {
    return (
      <div>
        <input type="text"/><button>{this.props.btnText}</button>
      </div>
    )
  }
}

state

组件内部状态定义用state,有两种定义方法 ,而props是外部传入的属性.只有class组件才有this和state

// App.js
export default class App extends Component {
// 第一张定义方式
//  state = {
//    title: '待办事项'
//  }
// 第二种定义方法
  constructor () {
    super()
    this.state = {
      title: '待办事项',
      desc: '完成今天所有的事'
    }
  }
  render() {
    return (
      <Fragment>
        <TodoHeader desc={this.state.desc}>
          {this.state.title}
        </TodoHeader>

组件的分类:

  • class组件和function组件
  • 受控组件、不受控组件、半受控组件(通过props传入属性,在组件内部没法修改的叫受控组件,通过state定义的状态不是外部传入的时不受控组件)
    不能跨组件传递props,必须一层一层的传
    组件模板渲染语法
{/* {this.todos[0].isCompleted && '完成'} */}
{this.state.todos[0].isCompleted ? '完成' : '未完成'}
        {
          this.state.todos.map(todo => {
            return <div key={todo.id}>{todo.title}</div>
          })
        }

{div dangerouslySetInnerHTML={{__html: this.state.article}}} // 渲染不带HTML标签的内容

dangerouslySetInnerHTML,类似于vue的v-html,输出不带HTML标签的内容


image.png

属性vs状态

相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)

不同点:

  1. 属性能从父组件获取,状态不能
  2. 属性可以由父组件修改,状态不能
  3. 属性能在内部设置默认值,状态也可以
  4. 属性不在组件内部修改,状态要改
  5. 属性能设置子组件初始值,状态不可以
  6. 属性可以修改子组件的值,状态不可以

state 的主要作用是用于组件保存、控制、修改自己的可变状态。state 在组件内1部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state 是一个局部的、只能被组件自身控制的数据源。state 中状态可以通过 this.setState方法进行更新,setState 会导致组件的重新渲染。

props 的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props,否则组件的 props 永远保持不变。

如果搞不清 stateprops 的使用场景,记住一个简单的规则:尽量少地用 state,多用 props

没有 state 的组件叫无状态组件(stateless component),设置了 state 的叫做有状态组件(stateful component)。因为状态会带来管理的复杂性,我们尽量多地写无状态组件,尽量少地写有状态的组件。这样会降低代码维护的难度,也会在一定程度上增强组件的可复用性。

更改状态(setState)

  • react不能直接通过this.state.xxx = !this.state.xxx来直接更改状态,要用setSate()方法来更改。(能修改数据,但是页面不会渲染)
  • setState()有两个参数,第一个参数有两种情况:第一种是一个对象;第二种情况是一个方法。
  • setState的第二个参数是一个回调在里面return一个对象,由于setState是异步的,想要获取最新的state要在第二个参数的回调里获取
  • react的setState是异步的,setState里的方法要比它外面的方法后执行
import React, { Component } from 'react'

export default class Like extends Component {
    constructor () {
        super()
        this.state = {
            isLiked: false
        }
    }
    likedClick = () => {
        // setState的第一种写法
        // this.setState({
        //     isLiked: !this.state.isLiked
        // })
        // 第二种写法是一个方法,可以直接传上次的状态prevState和props
        this.setState((prevState) => {
            console.log(prevState)
            return {
                isLiked: !prevState.isLiked
            }
        }, () => {
            // setState是异步的,想要获取最新的state要在这个回调里获取
            console.log(this.state)
        })
    }
  render() {
    return (
      <div>
        <span onClick={this.likedClick}>
            {
                this.state.isLiked ? '太菜了!👍' : '棒棒哒!👎'
            }
        </span>
      </div>
    )
  }
}

image.png

react事件

  • 如果要修改state里的值需要使用onChange事件(这是采用驼峰命名的),然后通过setState方法来修改值,否则页面上不能输入
// TodoInput/index.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
  static propTypes = {
    btnText: PropTypes.string
  }
  static defaultProps = {
    btnText: '添加TODO'
  }
  constructor () {
    super()
    this.state = {
      inputValue: 'xxx'
    }
    // this.addTodo = this.addTodo.bind(this)
  }
  onInputChange = (e) => {
    this.setState({
      inputValue: e.currentTarget.value
    })
  }
addTodo = (id) => {
  console.log(this.state,id)
  }
  render() {
    return (
      <div>
        <input type="text" onChange={this.onInputChange} value={this.state.inputValue}/>
        <button onClick={this.addTodo.bind(this,1234)}>{this.props.btnText}</button>
      </div>
    )
  }
}
// App.js
addItem = (todoTitle) => {
    console.log(todoTitle)
    // this.setState({
    //   // 添加todo的第一个方法,这里不能用push,push返回的是数组的长度
    //   todos: this.state.todos.concat({
    //     id: Math.random(),
    //     title: todoTitle,
    //     isCompleted: false
    //   })
    // })
    // 第二个方法,先把数组复制一份
    // const newTodos = this.state.todos.slice()
      const newTodos = [...this.state.todos]
    newTodos.push({
        id: Math.random(),
        title: todoTitle,
        isCompleted: false
    })
    this.setState({
      todos: newTodos
    })
  }

react里面通过ref获取组件或者dom元素,在使用ref之前要先调用React.createRef来创建ref,要在constructor里创建ref

import React, { Component, createRef } from 'react'
import PropTypes from 'prop-types'
export default class TodoInput extends Component {
  static propTypes = {
    btnText: PropTypes.string
  }
  static defaultProps = {
    btnText: '添加TODO'
  }
  constructor () {
    super()
    this.state = {
      inputValue: ''
    }
    // this.addTodo = this.addTodo.bind(this)
    // 在constructor里创建ref
    this.inputDom = createRef()
  }
  onInputChange = (e) => {
    this.setState({
      inputValue: e.currentTarget.value
    })
  }
  addKeyUp = (e) => {
    if(e.keyCode === 13){
      // console.log(e)
      this.addTodo()
    }
  }
  addTodo = () => {
    if(this.state.inputValue === ''){
      return
    }
    console.log(this.inputDom)
    this.props.addItem(this.state.inputValue)
    this.setState({
      inputValue: ''
    }, () => {
        this.inputDom.current.focus()
    })
  }
  render() {
    return (
      <div>
        <input type="text" onChange={this.onInputChange} onKeyUp={this.addKeyUp} value={this.state.inputValue} ref={this.inputDom}/>
        <button onClick={this.addTodo}>{this.props.btnText}</button>
      </div>
    )
  }
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • 作为一个合格的开发者,不要只满足于编写了可以运行的代码。而要了解代码背后的工作原理;不要只满足于自己的程序...
    六个周阅读 8,428评论 1 33
  • 40、React 什么是React?React 是一个用于构建用户界面的框架(采用的是MVC模式):集中处理VIE...
    萌妹撒阅读 1,005评论 0 1
  • HTML模版 之后出现的React代码嵌套入模版中。 1. Hello world 这段代码将一个一级标题插入到指...
    ryanho84阅读 6,222评论 0 9
  • 【转】react是一个js框架,类似于jquery,但是他做了很大的变化,它将利用jsx语法,将结构(html)和...
    悠哈121阅读 387评论 0 3
  • 1、什么是react React.js 是一个帮助你构建页面 UI 的库。React.js 将帮助我们将界面分成了...
    谷子多阅读 2,555评论 1 13