在React中state、props、Refs都是最基础的概念,本文将同时梳理下这三个知识点,主要内容包括:
React把每一个有状态的组件都看成是一个状态机,组件内部通过state来维护组件状态的变化。
在事件中触发setState()来修改state数据,state改变后会重新进行render()(React生命周期的内容,更多可点击)
在需要对用户输入、服务器请求或者时间变化等做出响应时,使用state。
React目前支持的事件列表:
还有些不常用的事件这里没有具体列出,如有兴趣可查看。
(1)React中的数据流是自上而下,从父组件流向子组件。
(2)子组件从父组件提供的props中获取数据,并进行渲染,一般是纯展示的组件。
(3)如果父组件的props更新,则该组件下面所有用到这个属性的子组件,都会重新进行render()(React生命周期的内容,更多可点击)
(4)props是只读的,props是只读的,props是只读的。不要试图修改props,否则会报错。
(5)propTypes进行类型检查:
校验类型包括基本类型、对象、数组、枚举。
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以将属性声明为 JS 原生类型,默认情况下
// 这些属性都是可选的
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
// 任何可被渲染的元素(包括数字、字符串、元素或数组)(或 Fragment) 也包含这些类型
optionalNode: PropTypes.node,
// 一个 React 元素
optionalElement: PropTypes.element,
// 你也可以声明 prop 为类的实例,这里使用JS 的 instanceof 操作符
optionalMessage: PropTypes.instanceOf(Message),
// 你可以让你的 prop 只能是特定的值,指定它为枚举类型
optionalEnum: PropTypes.oneOf(['News', 'Photos']),
};
除此之外,还可以对数组、对象类型做些比较深入的校验,如指定一个对象由特定的类型值组成。如果还不能满足需求,还可以自定义验证规则。
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 一个对象可以是几种类型中的任意一个类型
optionalUnion: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.instanceOf(Message)
]),
// 可以指定一个数组由某一类型的元素组成
optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
// 可以指定一个对象由某一类型的值组成
optionalObjectOf: PropTypes.objectOf(PropTypes.number),
// 可以指定一个对象由特定的类型值组成
optionalObjectWithShape: PropTypes.shape({
color: PropTypes.string,
fontSize: PropTypes.number
}),
// 你可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。
// 请不要使用 `console.warn` 或抛出异常,因为这在 `onOfType` 中不会起作用。
customProp: function (props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error(
'Invalid prop `' + propName + '` supplied to' +
' `' + componentName + '`. Validation failed.'
);
}
},
};
如果某个属性是必须的,那么就在类型后面加上isRequired:
import PropTypes from 'prop-types';
MyComponent.propTypes = {
// 你可以在任何 PropTypes 属性后面加上 `isRequired` ,确保
// 这个 prop 没有被提供时,会打印警告信息
requiredFunc: PropTypes.func.isRequired,
// 任意类型的数据
requiredAny: PropTypes.any.isRequired,
};
下面是父组件给子组件传递数据的示例:
父组件设置:
<Greeting name="Peter" />
子组件读取:
import React from 'react';
class Greeting extends React.Component {
render() {
return (
<div>Hello,{this.props.name}</div>
);
}
}
export default Greeting;
上述就是父组件设置name属性,子组件通过this.props.name读取。那如果从父组件要传递个age属性给子组件,可以继续在父组件中设置age属性:
父组件设置:
<Greeting name="Peter" age="16"/>
子组件读取:
import React from 'react';
class Greeting extends React.Component {
render() {
return (
<div>Hello,{this.props.name},age is {this.props.age}</div>
);
}
}
export default Greeting;
那如果还要继续传递gender、address、hobby等属性,还需要继续在父组件中一个个添加吗?
是可以写,但代码会写的比较多:
父组件设置:
const person = {
name: "Peter",
age: "16",
gender: "male",
address:"xxx",
hobby: "coding"
}
...
<Greeting
name={person.name}
age={person.age}
gender={person.gender}
address={person.address}
hobby={person.hobby}
/>
子组件获取:
import React from 'react';
class Greeting extends React.Component{
let {name,age,gender,address,hobby} = this.props;
render(){
return (
<div>Hello,{name},age is {age}</div>
<div>gender:{gender}</div>
<div>address:{address}</div>
<div>hobby:{hobby}</div>
);
}
}
export default Greeting;
实际上这里在父组件设置属性时,可以用...把属性一次性地传递给子组件。避免了上述写法中手动传递多个属性,导致代码要写得很长的情况。
父组件设置:
const person = {
name:"Peter",
age:"16",
gender:"male",
address:"xxx",
hobby: "coding"
}
...
<Greeting {...person} />
(1)可以用来访问在render()中的Dom节点
虽然也可以通过document.getElementById(“xxx”)来获取Dom节点,但React推荐不要直接操作Dom节点,只有用Refs才是访问组件内部Dom元素的唯一可靠方法。
(2)使用场景:
注意:不要滥用Refs。
(3)使用Refs的三种方式:
这种方式是比较老的用法了,React已明确表示这种用法已经过时,并且可能会移除掉,建议用回调函数或者React.createRef()(React 16.3之后引入的)的方式来访问refs,不过还是简单看下它的用法。
它的用法是这样的:
import React from 'react';
class Greeting extends React.Component {
constructor(props) {
super(props);
}
handleClick() {
this.refs.inputTest.focus();
}
render() {
return (
<div>
<input type="text" ref="inputTest" />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.handleClick.bind(this)}
/>
</div>
);
}
}
export default Greeting;
上述代码用回调函数的方式来实现:
import React from 'react';
class TextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = null;
this.setTextInput = ele => {
this.textInput = ele;
}
this.focusTextInput = () => {
if (this.textInput) {
this.textInput.focus();
}
}
}
render() {
return (
<div>
<input type="text" ref={this.setTextInput} />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.focusTextInput}
/>
</div>
);
}
}
export default TextInput;
ref传递的是一个函数:使用ref的回调函数,将text输入框的Dom节点存储到React。
如果是v16.3之后的React,那么可以使用这种方法。
创建Refs:
this.myRef = React.createRef();
通过ref属性来获取React元素
return <div ref={this.myRef} />;
访问Refs:
const node = this.myRef.current;
和回调函数传递一个函数不同,React.createRef()传递的是React.createRef()创建的ref属性。
上述代码用React.createRef()的方式来实现:
import React from 'react';
class TextInput extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
// 通过React.createRef();来创建Refs
this.textInput = React.createRef();
}
handleClick() {
// 通过 this.textInput.current访问Dom节点
this.textInput.current.focus();
}
render() {
return (
<div>
<input type="text" ref={this.textInput} />
<input
type="button"
value="点我输入框获取焦点"
onClick={this.handleClick}
/>
</div>
);
}
}
export default TextInput;
注意:
(1)可以在类组件上使用ref属性
(2)不能在函数组件上使用ref属性,因为函数组件没有实例。如果想在函数组件上使用ref属性,那就需要转换为类组件。
(3)可以在函数组件内部可以使用ref属性,只要它指向一个 DOM 元素或者 class 组件。
// 1可以在类组件上使用ref属性
class CustomTextInput extends React.Component {
// ...
}
class AutoFocusTextInput extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
componentDidMount() {
this.textInput.current.focusTextInput();
}
render() {
return (
<CustomTextInput ref={this.textInput} />
);
}
}
// 2不能在函数组件上使用ref属性,因为函数组件没有实例
function MyFunctionComponent() {
return <input />;
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.textInput = React.createRef();
}
render() {
// This will *not* work!
return (
<MyFunctionComponent ref={this.textInput} />
);
}
}
// 3可以在函数组件内部使用ref属性
function CustomTextInput(props) {
// textInput must be declared here so the ref can refer to it
let textInput = React.createRef();
function handleClick() {
textInput.current.focus();
}
return (
<div>
<input
type="text"
ref={textInput} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
createRef比回调函数看起来更加直观,不过也没有什么压倒性优势,只是希望提供一个更便捷的方法。
React 16.3还提供了一个名为React.forwardRef的API,主要是用于贯穿过父元素直接获取子元素的ref。这里的应用场景涉及HOC使用ref的问题,具体内容会在总结HOC相关内容时介绍。
最后再概况下state、props和refs:
state:把一个有状态的组件看成是一个状态机,组件内部通过state来维护组件状态的变化。
在DOM上注册事件,触发事件时通过setState()修改了state的数据,这会导致重新render()来更新虚拟DOM,虚拟DOM再转为DOM。
props:React中的数据流就像水流一样,自上而下,从父组件流向子组件。如同下图这个水竹一样的感觉,自上而下、层层传递地流淌。
Refs:获取render()中的DOM节点。
本文主要总结了React中state、props、refs的基础知识点,如有问题,欢迎指正。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。