React组件有很多属性,在其中有三个属性最为核心和重要:
state让你可以通过数据驱动视图,让视图自动根据数据的改变而改变。
props可以提高组件的灵活性,通过外界传值让组件根据用户特性而呈现不同的视图。
refs能够减少用户对DOM的直接操作,而可以通过获取代理节点而提高开发效率和页面性能。
如果需要在组件中维护一些数据,那么可以使用state集中管理这些数据,如果你学过vue,那么可以类比vue的data。
class ... {
constructor(props){
// 类中重写构造器函数,那么一定要首先调用一下super
super(props);
this.state = {
hello:'你好呀'
}
},
// 使用数据
render(){
return 。。。
}
}
state 是一个对象,以键值对的形式维护数据,初始为 null。
class ... {
constructor(props){
// 类中重写构造器函数,那么一定要首先调用一下super
super(props);
this.state = {
hello:'你好呀'
}
},
// 使用数据
render(){
return {this.state.hello}
},
say(){
console.log(this.state.hello);
}
}
为什么要用onClick
,因为react的规定
为什么这样写,会报错,说say中的 this 是 undefined?
onClick={this.say}
这是将onClick赋值为对方法的引用,等到点击的时候是通过onClcik调用而不是通过实例调用那么怎么解决这个问题呢?
// 在构造函数中添加这一语句
// 忘记了bind函数的请自行查找资料
this.say = this.say.bind(this);
但是!还有一个更好的解决方法
// 在类中这么定义
say = () => {
console.log(this.state.hello);
}
这样做,一切复杂的形式都不用再写了,直接这么写,然后正常使用onClick={this.say}
绑定事件即可。
所以,以后写函数,推荐所有所有的都是用将箭头函数赋值给属性的方式进行。
如果只是想在代码中修改数据,可以直接赋值修改,但是如果想要通过数据的更新达到页面同步更新的效果,就需要使用setState
函数。
this.setState({hello: "hellow"})
通过这个api,原状态集和修改输入参数是合并的关系,不需要更改的不需要配置。
当然不用,而且一般都不会这么做,可以直接作为属性赋值。
如果需要从外部传参到组件内部,那么这么写
ReactDOM.render( , doucment.getElementById('test'))
在渲染的时候通过标签的自定义属性传入想要的参数,然后就可以在组件中使用,使用方法:
class ... {
return {this.props.name}
}
很简单,直接通过this.props
就可以访问到了,而且这是一个只读的属性。
当需要传入的参数太多了,不可能一个个去写吧?所以提供了一个很方便的用法:
const x = {..........} // 有很多很多键值对
ReactDOM.render( )
通过解构赋值,可以一下子把具有很多很多属性的对象直接解构成组件标签的自定义属性。
(键值对的键和自定义的属性名字一样)
如果想要限制传入值的类型,比如只想要传入字符串,并且添加默认值,有两种方法:
外部添加:
class Person extends React.Component {
...
}
Person.propTypes = {
name: PropTypes.string
// 如果需要指定为必传参数在string后面跟 .isRequired
}
Person.defaultProps = {
name: "无名氏"
}
内部添加:使用static关键字
class Person ... {
static propTypes = {
name: PropTypes.string.isRequired
}
static defaultTypes = {
name: "张三"
}
}
函数式组件可以接收一个props参数:
function Person (props) {
const {name, age} = props;
return (
{ name } - { age }
)
}
// 对props限制
Person.propTypes = {
name: PropTypes.string.isRequired
}
Person.defaultProps = {
name: "无名氏"
}
class ... {
render(){
return (
)
}
}
通过在虚拟DOM上使用ref
属性标记一个标签,可以后续从实例身上通过refs
属性获取到,这个虚拟DOM转化成真实DOM后的节点。
比如:
const {input1} = this.refs;
console.log(input1.value);
String类型的ref,现在已经抛弃了,因为他存才一些问题:
效率
字符串形式的ref最大的问题就是效率,相关问题可以去查看React官网,这里就不介绍了
class ... {
render(){
return (
this.input1 = c } />
)
}
}
过程是这样的:
当解析DOM的时候,会自动把 ref 对应的节点对象传入后面的回调形参里面,通过箭头函数,可以将 this 指向实例组件实例对象,并且将节点作为一个属性挂到组件实例对象的身上。
执行次数问题
如果ref回调函数是使用内联形式使用的,那么在 ref 更新的过程中会被执行两次,第一次传入参数null,第二次才会传入参数DOM元素。
这是因为在每次渲染的时候会创建一个新的函数实例,所以React清空旧的ref并且设置一个新的ref。通过将ref的回调函数定义成class的绑定函数的方式可以避免上述问题,但大多数情况下他是无关紧要的。
为什么第一次会传入null呢?因为当页面更新的时候会重新调用render,重新调用这个回调,但是不知道上一次这个回调的工作内容,为了确保回调的上一次运行不会影响这一次,就会传入一个null以清空。
如果想要避免这个问题,可以换一种写法,即使用类方法作为回调:
class ... {
getRef = (c) => {
this.input1 = c;
},
render(){
return (
)
}
}
上面这两种方法,基本可以认为唯一的区别就是调用次数不一样,但是这个问题几乎没有影响。
React.createRef 调用之后会返回一个容器,该容器可以存储被 ref 所标识的节点。
class ... {
myRef = React.createRef();
// 之后可以通过 myRef.current取出
render(){
return (
)
}
}
注意!该容器是专人专用的,不可以放多个,如果存入多个,那么后面存入的总会覆盖前面存入的!
目前这种方式是最新的,也是官方推荐的。