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
整个单页应用的入口文件:App.js
入口文件:index.js
其余的子组件包在App下面
-
在src下新建一个components目录,里面包含所有的子组件,一个子组件就是一个文件夹,用组件名作为文件夹名字;同时,在components的根目录下还有一个index.js文件,用来引入和导出所有的子组件,在App.js里面引入所有的组件。
在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标签的内容
属性vs状态
相似点:都是纯js对象,都会触发render更新,都具有确定性(状态/属性相同,结果相同)
不同点:
- 属性能从父组件获取,状态不能
- 属性可以由父组件修改,状态不能
- 属性能在内部设置默认值,状态也可以
- 属性不在组件内部修改,状态要改
- 属性能设置子组件初始值,状态不可以
- 属性可以修改子组件的值,状态不可以
state
的主要作用是用于组件保存、控制、修改自己的可变状态。state
在组件内1部初始化,可以被组件自身修改,而外部不能访问也不能修改。你可以认为 state
是一个局部的、只能被组件自身控制的数据源。state
中状态可以通过 this.setState
方法进行更新,setState
会导致组件的重新渲染。
props
的主要作用是让使用该组件的父组件可以传入参数来配置该组件。它是外部传进来的配置参数,组件内部无法控制也无法修改。除非外部组件主动传入新的 props
,否则组件的 props
永远保持不变。
如果搞不清 state
和 props
的使用场景,记住一个简单的规则:尽量少地用 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>
)
}
}
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>
)
}
}