- React元素的属性名使用驼峰命名法
- 特殊属性名:class -> className for -> htmlFor tabindex -> tabIndex
- 没有子节点的React元素可以用/>结束
- 推荐:使用小括号包裹JSX,从而避免JS中的自动插入分号陷阱
const dv = (
hello
)
- jsx中是使用Javascript表达式
- 数据储存在JS中
- 语法:{JavaScript表达式} 注意不是双大括号!
const name = 'jack'
const dv = (
你好,我叫{name}
)
6.条件渲染
// 条件渲染
const isLoading = true
const loadData = () => {
if(isLoading){
return loading...
}
return 数据加载完成,此处显示加载后的数据
}
const title = (
条件渲染:{loadData()}
)
ReactDOM.render(title,document.getElementById('root'))
还可以使用if/else或三元运算符或逻辑与运算符来实现
//三元表达式
const loadData = () => {
return isLoading ? (loading...) : (数据加载完成,此处显示加载后的数据)
}
//逻辑与运算符
const loadData = () => {
return isLoading && (loading...)
}
isLoading是false不会执行后面的,true才会执行
7.列表渲染
const songs = [
{name:'qwe',id:1},
{name:'dfddg',id:2},
{name:'hsd',id:3}
]
const title = (
{songs.map(item => - {item.name}
)}
)
注意:
- 列表要渲染一组数据,应该使用map()方法
- 列表渲染应该添加key属性,key属性的值要保证唯一
- 原则:map()遍历谁,就给谁添加key属性
- 注意:尽量避免使用索引号作为key,索引号可变不稳定
8.JSX样式处理
-
- 行内样式--style
const list = (
jsx样式处理
)
-
- className样式
//导入
import './index.css'
const list = (
jsx样式处理
)
//index.css
.title{
text-align: center;
}
9.react组件的两种创建方式
- 9.1 使用函数创建组件
- 使用js函数或者箭头函数创建
- 函数名首字母大写,react以此区分 组件 和普通的 元素
- 函数要有return值
- 渲染函数组件:用函数名作为组件标签名
- 组件标签可以是单标签也可以是双标签
function Hello(){
return(
我的第一个react组件!
)
}
ReactDOM.render( ,document.getElementById('root'))
箭头函数
const Hello = () => 我的第一个react组件
- 9.2 使用类创建组件
类组件:使用ES6的class创建的组件
约定1:类名也必须以大写字母开头
约定2:类名称应该继承React.Component父类,从而可以使用父类中提供的方法或属性
约定3:类组件必须提供render()方法
约定4:render()方法必须有返回值,表示该组件的结构
class Hello extends React.Component{
render(){
return(
我的第一个react类组件!
)
}
}
9.3 抽离为独立的js文件
组件作为独立的个体,独立存放一个组件
- 创建Hello.js文件
- 在文件中导入react
- 创建组件
- 在Hello.js中导出该组件
- 在index.js中导入该组件
- 渲染组件
10.点击事件:驼峰命名法,
- 函数组件绑定事件
// 通过函数组件绑定事件
function App() {
function handleClick(){
console.log('函数组件中的事件绑定,事件触发了')
}
return(
)
}
ReactDOM.render( ,document.getElementById('root'))
- 类组件
class App extends React.Component{
handleClick(){
console.log('单击事件触发了')
}
render(){
return(
)
}
}
ReactDOM.render( ,document.getElementById('root'))
11.事件对象
- 通过时间处理程序获取到事件对象
- react中的事件对象叫做:合成事件(对象)
- 合成事件:兼容所有浏览器,无需担心跨浏览器兼容性问题
12.有状态组件和无状态组件
- 函数组件又叫做无状态组件,类组件又叫做有状态组件
- 状态(state)即数据
- 函数组件没有自己的状态,只负责数据展示(静)
- 类组件有自己的状态,负责更新UI,让页面”动“起来
13.组件中的state和setState
- 状态(state)即数据,是组件内部私有数据
- state的值是对象,表示一个组件中可以有多个数据
- 状态改变,语法:this.setState({要修改的数据}),注意,不要直接修改state中的值,这是错误!!!
this.setState({
number: this.number + 1
})
- setState()作用:1.修改state 2.更新UI
- 思想:数据驱动视图
14.抽离onclick的方法,解决this指向问题
- 1.箭头函数,利用箭头函数自身不绑定this的特点
class App extends React.Component{
state = {
count: 0,
}
onIncrement() {
this.setState({
count: this.state.count + 1
})
}
render(){
return(
计数器:{this.state.count}
//箭头函数中的this指向外部环境,此处为:render()方法
)
}
}
- 2.Function.prototype.bind()
利用ES5中的bind方法,将事件处理程序中的this与组件实例绑定到一起
class App extends React.Component{
state = {
count: 0,
}
constructor() {
super()
this.onIncrement = this.onIncrement.bind(this)
}
onIncrement() {
this.setState({
count: this.state.count + 1
})
}
render(){
return(
计数器:{this.state.count}
//箭头函数中的this指向外部环境,此处为:render()方法
)
}
}
- 3.class实例方法,推荐使用
利用箭头函数形式的class实例方法。注意:该语法是实验性语法,但是,由于babel的存在可以直接使用
class App extends React.Component{
state = {
count: 0,
}
onIncrement = () => {
this.setState({
count: this.state.count + 1
})
}
render(){
return(
计数器:{this.state.count}
)
}
}
15.表单处理
- 1.受控组件
-
- 由state的值来控制表单元素的值
-
- 步骤:
-
-
- 1.在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
-
-
-
- 2.给表单元素绑定change事件,将表单元素的值设置为state的值(控制表单元素的变化)
-
class App extends React.Component{
state = {txt:''}
handleChange = e => {
this.setState({
txt:e.target.value
})
}
render(){
return(
+1
)
}
}
- 2.多表单元素优化
-
- 1.给表单增加name属性,名称与state相同
-
- 2.根据表单元素类型获取对应值
-
- 3.在change事件处理程序中通过[name]来修改对应的state
//根据表单元素类型获取值
const value = target.type === "checkbox"
? target.checked
: target.value
//根据name设置对应state
this.setState({
[name]: value
})
- 3.非受控组件
借助于ref,使用原生DOM方式来获取表单元素值
ref作用:获取DOM或组件
使用步骤: -
- 1.调用React.createRef()方法创建一个ref对象
construction(){
super()
this.txtRef = React.reacteRef()
}
-
- 2.将创建好的ref对象添加到文本框中
-
- 3.通过ref对象获取到文本框的值
console.log(this.txtRef.current.value)
16.组件的props
- 组件是封闭的,要接收外部数据应该通过props来实现
- props的作用:接收传递给组件的数据
- 传递数据:给组件标签添加属性
- 接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据
父传子
特点: - 可以给组件传递任意类型的数据
- props是只读的对象,只能读取属性的值,无法修改对象
// 2.接收数据
const Hello = (props) => {
console.log(props)
props.fn()
// 修改props的值:错误演示!!!!!!!
// props.name = '123'
return(
props:{props.name}
{props.tag}
)
}
// 1.传递数据
ReactDOM.render(console.log('这是一个函数')} tag={这是一个p标签
} />,document.getElementById('root'))
// 2.接收数据
class Hello extends React.Component{
constructor(props){
super(props)
console.log(props)
props.fn()
}
render(){
return(
props:{this.props.age}
)
}
}
// 1.传递数据
ReactDOM.render( ,document.getElementById('root'))
- 注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数constrauctor中获取到props!
class Hello extends React.Component{
//推荐将props传递给父类构=构造函数
constrauctor(props){
super(props)
}
render(){
return (
props:{this.props.age}
)
}
}
子传父
-
- 1.父组件提供一个回调函数(用于接收数据)
-
- 2.自组件调用传入的函数
兄弟组件传递
状态提升,由兄弟组件共同的父组件管理状态
17.Context
跨组件传递数据
使用步骤:
- 2.自组件调用传入的函数
- 1.调用React.createCcontext()创建Provider(提供数据)和Consumer(消费数据)两个组件
const { Provider, Consumer } = React.createContext()
- 2.使用Provider组件作为父节点
- 3.设置value属性,表示要传递的数据
- 4.调用Consumer组件接收数据
{ data => data参数表示接收到的数据 -- { data } }
18.props深入
18.1 children属性:表示组件标签的子节点,当组件标签有子节点时,props就会有该属性
function Hello (props){
return(
组件的子节点:{ props.chihldren }
)
}
< Hello>我是子节点
18.2 props校验:允许在创建组件的时候,就指定props的类型、格式等
- 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据
- 传入到的数据格式不对,可能会导致报错
- 作用:捕获使用组件时因为props导致的错误,给出明确的错误提示,增加组件的健壮性
App.propTypes = {
colors: PropTypes.array
}
使用步骤:
-
- 安装包prop-types(yarn add prop-types/npm i prop-types)
-
- 导入prop-types包
import PropTypes from 'prop-types'
-
- 使用 组件名.propTypes = {} 来给组件的props添加校验规则
约束规则:
1.常见类型:array、bool、func、number、object、string
2.react元素类型:element
3.必填项:isRequired
4.特定结构的对象:shape({})
- 使用 组件名.propTypes = {} 来给组件的props添加校验规则
//添加props校验
//属性a的类型 数值
//属性fn的类型 函数并且为必填项
//属性tag的类型 React元素(element)
//属性filter的类型 对象({area: '上海', price: 1999})
App,propTypes = {
a: Proptypes.number,
fn: Proptypes.func.isRequired,
tag: Proptypes.element,
filter: Proptypes.shape({
area: Proptypes.string,
price: Proptypes.number
})
}
18.3 props默认值:未传入props时生效
App.defaultProps = {
pageSize:0
}
- 组件的生命周期
19.1 意义:组件的生命周期有助于理解组件的运行方式,完成更复杂的组件功能,分析组件错误原因等
组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用卸载的过程
生命周期的每个阶段总是伴随着一些方法调用,这些方法就是生命周期的钩子函数
钩子函数 的作用:为开发人员在不同阶段操作组件提供了时机
只有 类组件 才有生命周期
19.2 生命周期的三个阶段
-
-
创建阶段(挂在阶段)
执行时机:组件创建时(页面加载时)
执行顺序:
-
-
2.更新阶段
执行时机:1.setState() 2.forceUpdate() 3.组件接收到新的props
说明:以上三者任意一种变化,组件就会重新渲染
执行顺序:
在componentDidUpdate中调用this.setState时需要加if条件语句判断
//比较更新前后的props是否相同,来决定是否重新渲染组件
if(prevProps.count !== this.props.count){
this.setState({})
//或者发送ajax请求的代码
}
-
-
卸载阶段
执行时机:组件从页面消失
-
- render-props和高阶组件
20.1 React组件复用
复用什么:1.state 2. 操作state的方法(组件状态逻辑)
两种方式:1.render props模式 2. 高阶组件(HOC)
-
- .render props模式
//调用
{
... ...
}}>
//组件内部
render(){
return this.props.render(this.state)
}
children代替render属性
//调用
{() => {
... ...
}}
//组件内部
this.props.children(this.state)
组件优化:
//添加校验
Mouse.propsTypes = {
children: PropTypes.func.isRequired
}
代码优化:
1.给render props模式添加props校验
2.应该在组件卸载时,解除mousemove事件绑定
Mouse.propsTypes = {
children: PropTypes.func.isRequired
}
componentWillUnmount(){
window.removeEventListener('mousemove',this.handleMouseMove)
}
20.2高阶组件
目的:实现状态逻辑复用
使用步骤:
1.创建一个函数,名称约定以with开头
2.指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
3在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
4.在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
5.调用改高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
function withMouse(WrappedComponent){
class Mouse extends React.Component {}
return Mouse
}
//Mouse组件中的render方法
return
//创建高阶组件
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
}
}
//设置displayName
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
return Mouse
}
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
//用来测试高阶组件
const Position = props =>{
return (
鼠标当前位置:(x:{props.x},y:{props.y})
)
}
//获取增强后的组件:
const MousePosition = withMouse(Position)
class App extends React.Component{
render() {
return(
高阶组件
)
}
}
设置displayName
- 使用高阶组件存在的问题:得到的两个组件名称相同
- 原因:默认情况下,React使用组件名称作为displayName
- 解决方式:为高阶组件设置displayName便于调试时区分不同的组件
- displayName的作用:用于设置调试信息(React Developer Tools信息)
Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
function getDisplayName(WrappedComponent){
return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}
传props:
render(){
return
}
//组件MousePosition 传入的参数就不会丢失了
21.setState说明
21.1异步更新
调用多次setState,只会触发一次render渲染
21.2推荐语法
this.setState((state,props)=>{
return{
count:state.count + 1
}
})
回调函数形式的setState更新也是异步更新,立刻打印state,结果还是1。
连续两次更新state加1,第二次是在第一次基础上加一的,即第二次拿到的值是2,第二次更新是在2上➕1
21.3
setState第二个参数
- 场景:在状态更新(页面完成重新渲染)后立即执行
- 语法:setState(updater,[,callback])
this.setState(
(state,props)=>{},
()=>{console.log('这个回调函数会在状态跟新后立即执行')}
)