文章目录
- 1 组件
- 1.1 函数组件
- 1.2 类式组件
- 2 组件三大核心属性state-props-refs
- 2.1 state
- 2.1.1 关于state的理解
- 2.1.2 类式组件中的使用state
- 2.2 props
- 2.2.1 关于props的理解
- 2.2.2 类式组件中使用 props
- 2.2.3 函数式组件使用props
- 2.2.4 props和state的区别
- 2.3 refs
- 2.3.1 关于props的理解
- 2.3.2 操作refs的方法
- 3 事件处理与条件渲染
- 3.1 事件处理
- 3.2 条件渲染
- 4 列表与表单
- 4.1 列表
- 4.2 表单
- 5 状态提升与组合
- 5.1 状态提升
- 5.2 组合
- 6 高阶函数、函数柯里化、高阶组件
- 6.1 高阶函数
- 6.2 函数的柯里化
- 6.3 高阶组件
1 组件
组件
:从概念上类似于 JS 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
React-dom是React剥离出的涉及DOM操作的部分
DOM (文档对象模型)
,是用来呈现
以及与任意HTML或XML文档交互
的API
。 DOM 是载入到浏览器中的文档模型,以节点树的形式来表现文档
,每个节点代表文档的构成部分(页面元素、字符串或注释等)。
BOM是浏览器对象模型,是对浏览器本身进行操作,DOM是文档对象模型,是对浏览器(可看成容器)内的内容进行操作。
1.1 函数组件
函数组件
:是一个有效的 React 组件,它本质上就是 JavaScript 函数,因为它接收
唯一带有数据的 props
(代表属性)对象与并返回一个 React 元素
。
// 1. 创建函数式组件
function MyComponent(props){//入参console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式return <h2>Hello,{props.name}</h2>
}
// 当 React 元素为用户自定义组件时,它会将JSX所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。
// 传参
const element = <MyComponent name="Sara" />;
// 2. 渲染组件到页面
ReactDOM.render(element,document.getElementById('root')
);
执行了ReactDOM.render
之后,发生了什么?
- 调用
ReactDOM.render()
函数,并传入<MyComponent name="Sara" />
作为参数。 - React 调用
MyComponent 组件
,并将{name: 'Sara}
作为 props 传入。 MyComponent
组件将Hello, Sara
元素作为返回值。- React DOM 将返回的虚拟DOM转为真实DOM,呈现在页面中,高效地更新为
Hello, Sara
。
相关补充:严格模式中的this:
//如果不开启严格模式,直接调用函数,函数中的this指向window
//如果开启了严格模式,直接调用函数,函数中的this是undefinedfunction sayThis() {console.log(this)
}
sayThis() // Window {...}function sayThis2() {'use strict'console.log(this)
}
sayThis2() // undefined
注:
- 组件名必须首字母大写
- 虚拟DOM元素只能有一个根元素
- 虚拟DOM元素必须有结束标签
1.2 类式组件
//1.创建类式组件
class MyComponent extends React.Component {render(){//render放在MyComponent的原型对象上,供实例使用。this指向MyComponent组件实例对象。console.log('render中的this:',this);return <h2>Hello,{props.name}</h2>}
}
const element = <MyComponent name="Sara" />;
// 2. 渲染组件到页面
ReactDOM.render(element,document.getElementById('root')
);
执行了ReactDOM.render
之后,发生了什么?
- React解析组件标签,找到了MyComponent组件。
- 发现组件是使用类定义的,随后new出来该类的实例,并通过该实例调用到原型上的render方法。
- 将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
从图中可以看出,MyComponent组件实例对象中包含state、props、refs三大属性
相关补充:ES6中类的注意事项:
- 类中的构造器不是必须要写的,要对实例进行一些初始化的操作,如添加指定属性时才写。
- 如果A类继承了B类,且A类中写了构造器,那么A类构造器中的
super
是必须要调用的。 - 类中所定义的方法,都放在了类的原型对象上,供实例去使用。
2 组件三大核心属性state-props-refs
2.1 state
2.1.1 关于state的理解
- React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
state
是在组件内被组件自己管理的(类似于在一个函数内声明的变量), 简单说就是该组件所存储的数据(状态) state 是私有的,并且完全受控于当前组件,state值是对象(可以包含多个key-value
的组合)。- state 代表了
随时间会产生变化的数据
,应当仅在实现交互时使用
, 组件可以选择把它的 state 作为 props 向下传递到它的子组件中 - 在React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
2.1.2 类式组件中的使用state
class Weather extends React.Component{constructor(props) {super(props)//构造器是否接收props,是否传递给super,取决于:是否希望在构造器中通过this访问prop//在构造函数中定义state也可以// this.state = {weather: "炎热"} }//定义state 给类的实例对象添加属性且赋值state = {weather: "炎热"}render() {// 读取状态// this为组件实例对象return <h1>今天天气很{this.state.weather}</h1>}
}
ReactDOM.render(<Weather/>, document.getElementById('test'))
类式组件中定义state的方法:
-
在构造器中定义state
-
在类中添加属性state定义
如何使用state:
- 通过
this.state
调用里面的值
如何修改 state?
-
在类式组件的函数中,不能直接修改
state
值// 不允许 this.state.weather = '凉爽'
-
通过类的原型对象上的方法
setState()
,setState是一个同步的方法,但是setState引起React后续更新状态的动作是异步的,setState
是一种合并操作,不是替换操作//partialState: 状态改变对象 callback: 可选的回调函数,状态更新完毕后调用 this.setState(partialState, [callback])// 写法一:直接修改 页面为“凉爽”,但是拿到的状态还是“炎热” this.setState({weather: "凉爽" })//写法二:在更新完状态后拿到该状态,使用回到函数 this.setState({state.weather: "凉爽"},()=>{});
2.2 props
2.2.1 关于props的理解
-
每个组件对象都会有
props
(properties)属性,组件标签的所有属性都保存在props中 -
props
是传递给组件的(类似于函数的形参),是父组件向子组件传递数据的方式 -
props是通过
标签属性
从组件外
向组件内
传入的数据(从外部传入组件内), 且组件内不可修改
props数据,是只读的
2.2.2 类式组件中使用 props
class Person extends React.Component{render() {return (<ul>// 入参<li>姓名:{this.props.name}</li> <li>性别:{this.props.sex}</li> <li>年龄:{this.props.age}</li></ul>)}
}//对标签属性进行类型、必要性的限制
static.propTypes = {name:PropTypes.string.isRequired, // 限制name必传,且为字符串sex:PropTypes.string, // 限制sex为字符串age:PropTypes.number, // 限制age为数值speak:PropTypes.func, // 限制speak为函数
}
// 指定默认的标签属性
//指定默认标签属性值
static.defaultProps = {sex:'男', // sex默认值为男age:18 //age默认值为18
}// 传参
const element = <Person name: 'zs', age: 18, sex: '男'/>ReactDOM.render(element, document.getElementById('test'))
在使用的时候可以通过 this.props
来获取值 类式组件的 props
:
-
通过在组件标签上传递值,在组件中就可以获取到所传递的值
-
在构造器里的
props
参数里可以获取到props
-
可以分别设置
propTypes
和defaultProps
两个属性来分别操作props
的规范和默认值,两者都是直接添加在类式组件的原型对象上的(所以需要添加static
) (需引入prop-types
库) -
可以通过
...
运算符来简化const p = {name: 'zs', age: 18, sex: '男'} const element = <Person {...p}/>
2.2.3 函数式组件使用props
函数组件的 props
定义:
- 函数在使用
props
的时候,是作为入参进行使用的 - 在组件标签中传参
- 对
props
的限制和默认值同样设置在原型对象上
//创建组件
function Person (props){//入参//从props解构出对应参数const {name,age,sex} = propsreturn (<ul><li>姓名:{name}</li><li>性别:{sex}</li><li>年龄:{age}</li></ul>)
}// 传参
const element = <Person name: 'zs', age: 18, sex: '男'/>ReactDOM.render(element, document.getElementById('test'))
2.2.4 props和state的区别
-
props
是传递给组件的(类似于函数的形参),是父组件向子组件传递数据的方式 -
state
是在组件内被组件自己管理的(类似于在一个函数内声明的变量), 代表了随时间会产生变化的数据,应当仅在实现交互时使用 -
组件可以选择把它的 state 作为 props 向下传递到它的子组件中
-
state
是组件自身的状态,而props
则是外部传入的数据
2.3 refs
2.3.1 关于props的理解
- 组件内的标签可以定义
ref
属性来标识自己 - ref提供了一种方式,允许我们访问 DOM 节点或在
render
方法中创建的 React 元素。
2.3.2 操作refs的方法
-
字符串形式(已过时)
//定义 <input ref="input1"/> //使用 this.refs.input1
-
回调形式
//定义 <input ref={ c => this.input1 = c } /> //使用 this.input1
-
createRef形式(推荐):React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的
//定义 myRef = React.createRef() <input ref={this.myRef}/> //使用 this.myRef.current
注意
: 不要过度的使用 ref,如果发生时间的元素刚好是需要操作的元素,就可以使用事件对象 event.target 去替代。
3 事件处理与条件渲染
3.1 事件处理
原则一:通过onXxx属性指定事件处理函数(注意大小写)
- React使用的是
自定义事件
,采用小驼峰式(camelCase), 使用JSX 语法时传入一个函数 - React中的事件是通过
事件委托
方式处理的(委托给组件最外层的元素),更高效
// 传统DOM元素
<button onclick="activateLasers()">Activate Lasers
</button>// React元素
<button onClick={activateLasers}>Activate Lasers
</button>
原则二:通过event.target得到发生事件的DOM元素对象
- 当发生事件的元素是需要操作的元素时,可以通过event.target得到发生事件的DOM元素对象,避免使用
ref
//创建组件
class Demo extends React.Component{//创建ref容器myRef = React.createRef()//展示左侧输入框的数据showData = (event)=>{console.log(event.target); // <button>点我提示左侧的数据</button>alert(this.myRef.current.value);}//展示右侧输入框的数据showData2 = (event)=>{alert(event.target.value);}render(){return(<div><input ref={this.myRef} type="text" placeholder="点击按钮提示数据"/> <button onClick={this.showData}>点我提示左侧的数据</button> <input onBlur={this.showData2} type="text" placeholder="失去焦点提示数据"/> </div>)}
}
//渲染组件到页面
ReactDOM.render(<Demo />,document.getElementById('test'))
3.2 条件渲染
条件渲染
: 在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容 。
方法一: 声明一个变量并使用 if 语句
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(//传参<Greeting isLoggedIn={false} />,document.getElementById('root')
);
方法二:与运算符 &&
通过花括号包裹代码,你可以在 JSX 中嵌入任何表达式,这其中也包括与运算符 &&
表达式1 && 表达式2
-
第一个表达式的值为真,则返回表达式2的值
-
第一个表达式的值为假,则返回表达式1 的值
function Mailbox(props) {const unreadMessages = props.unreadMessages;return (<div>{unreadMessages.length > 0 &&<h2>You have {unreadMessages.length} unread messages.</h2>}</div>);
}const messages = ['React', 'Re: React', 'Re:Re: React'];
ReactDOM.render(<Mailbox unreadMessages={messages} />,document.getElementById('root')
);
方法三:三元表达式
三元表达式
:条件表达式 ? 表达式1 : 表达式2
如果条件表达式结果为真,则返回表达式1的值,为假,则返回表达式2的值
function UserGreeting(props) {return <h1>Welcome back!</h1>;
}
function GuestGreeting(props) {return <h1>Please sign up.</h1>;
}function Greeting(props) {return {props.isLoggedIn ? <UserGreeting /> : <GuestGreeting />}
}ReactDOM.render(<Greeting isLoggedIn={false} />,document.getElementById('root')
);
4 列表与表单
4.1 列表
在 React 中,把数组转化为元素列表的过程与JS中相似。
渲染基础列表组件
- 使用
{ }
在 JSX 内构建一个元素集合, 使用JS 中的map( )方法来遍历 numbers 数组。将数组中的每个元素变成标签。 key
是在创建元素数组时,需要用到的一个特殊字符串属性。key 帮助 React 识别出被修改、添加或删除的 item。应当给数组内的每个元素都设定 key,以使元素具有固定身份标识。因此你应当给数组中的每一个元素赋予一个独一无二的标识。 通常我们使用数据中的 id 来作为元素的 key 。
function NumberList(props) {const numbers = props.numbers;return (<ul>{numbers.map((number) => //在 map()方法中的元素需要设置key属性<ListItem key={number.toString()} value={number} />)}</ul>);
}const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(<NumberList numbers={numbers} />,document.getElementById('root')
);
4.2 表单
收集表单数据,包含表单元素的组件分为受控组件
与非受控组件
:
受控组件
: 页面中输入类的DOM (如input、textarea、select), 随着输入的过程,将数据存储在状态state
中,需要用的时候在从状态state中取 (随时更新
) ,比如在输入框输入用户和密码,随输入随展示数据 ,受控组件类似Vue中双向绑定( 数据变化更新视图,视图变化更新数据 )。非受控组件
: 页面中输入类的DOM在有需求的时候才存储到状态中(现用现取
),比如在输入框输入用户名和密码,点击登录触发回调才会获取数据
受控组件实例
// 创建组件
class Login extends React.Component {// 初始化状态state = {username: '',password: ''}// 保存用户名到状态中saveUsername = (event) => {this.setState({username: event.target.value})}// 保存密码到状态中savePassword = (event) => {this.setState({password: event.target.value})}// 表单提交的回调handleSubmit = (event) => {event.preventDefault()const {username, password} = this.statealert(`您输入的用户名是 ${username},您输入的密码是:${password}`)}render() {return (<form action="https://www.baidu.com/" onSubmit={this.handleSubmit}>用户名:<input onChange={this.saveUsername} type="text" name="username" />密码:<input onChange={this.savePassword} type="password" name="password" /><button>登录</button> </form>)}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'))
非受控组件实例
// 创建组件
class Login extends React.Component {handleSubmit = (event) => {event.preventDefault() // 阻止表单提交const {username, password} = thisalert(`您输入的用户名是 ${username.value},您输入的密码是:${password.value}`)}render() {return (<form action="https://www.baidu.com/" onSubmit={this.handleSubmit}>用户名:<input ref={c => this.username = c} type="text" name="username" />密码:<input ref={c => this.password = c} type="password" name="password" /><button>登录</button> </form>)}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'))
5 状态提升与组合
5.1 状态提升
状态提升
:在 React 中,将多个组件中需要共享的 state 向上移动到它们的最近共同父组件中,React 的数据是自上而下的流动的,多个组件便可实现共享 state。这就是所谓的“状态提升”
概括
: 子组件调用父组件方法改变父组件的state,从而改变子组件。(注:它本身或其它组件)
实现动态组件
:考虑好数据的存放位置,数据是一个组件在用,还是一些组件在用
一个组件在用
:放在组件自身即可一些组件在用
:放在他们共同的父组件上(状态提升)实现交互
:从绑定事件开始
// 父组件
class Father extends React.Component {constructor(props) {super(props)this.state = {value: '',}}valueChange(value) {this.setState({value})}render() {return (<div style={{ padding: '100px' }}>{/* 传参 */}<Child1value={this.state.value}onValueChange={this.valueChange.bind(this)}/><br /><Child2 value={this.state.value} /></div>)}
}// 子组件1
class Child1 extends React.Component {// 入参constructor(props) {super(props)}handle(e) {this.props.onValueChange(e.target.value)}render() {return <input value={this.props.value} onChange={this.handle.bind(this)} />}
}// 子组件2
class Child2 extends React.Component {// 入参constructor(props) {super(props)}render() {return <input value={this.props.value} />}
}ReactDOM.render(<Father />, document.getElementById('root'))
5.2 组合
**React官方推荐使用组合
来实现组件间的代码重用, 组件可以接受任意 props,包括基本数据类型,React 元素以及函数。 **
包含关系
function FancyBorder(props) {return (//props包括属性和子组件children<div className={'FancyBorder FancyBorder-' + props.color}>{props.children}</div>);
}
function WelcomeDialog() {return (const color="blue"// 传参不仅可以传递属性(color="blue"常量 color={color}变量),还可以传递子组件children<FancyBorder color="blue" //color={color}><h1 className="Dialog-title">Welcome</h1><p className="Dialog-message">Thank you for visiting our spacecraft!</p></FancyBorder>);
}
少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用 children
,而是自行约定:将所需内容传入 props,并使用相应的 prop。
<Contacts />
和 和 <Chat />
之类的 React 元素本质就是对象(object),所以你可以把它们当作 props,像其他数据一样传递。
function SplitPane(props) {return (<div className="SplitPane"><div className="SplitPane-left">{props.left}</div><div className="SplitPane-right">{props.right}</div></div>);
}function App() {return (<SplitPaneleft={<Contacts />}right={<Chat />} />);
}
6 高阶函数、函数柯里化、高阶组件
6.1 高阶函数
高阶函数
:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数。
- 若A函数,接收的参数是一个函数,那么A就可以称之为高阶函数。
- 若A函数,调用的返回值依然是一个函数,那么A就可以称之为高阶函数。
常见的高阶函数有:Promise
、setTimeout
、arr.map()
等等
6.2 函数的柯里化
函数的柯里化
:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum1(a, b, c){return a + b + c;
}
sum1(1, 2, 3)// 柯里化后
function sum(a){return(b)=>{return (c)=>{return a+b+c}}
}
sum(1)(2)(3)
6.3 高阶组件
高阶组件(HOC)
是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
具体而言,高阶组件是参数为组件,返回值为新组件的函数。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
组件是将 props 转换为 UI,而高阶组件是将组件转换为另一个组件。