前言
梦想注定是孤独的旅程,路上少不了质疑和嘲笑,但那又怎样,哪怕遍体鳞伤也要勇往直前。有梦别怕痛,想赢别喊停!
1.有状态组件
<div id="root"></div>
<script type="text/babel">
class Hello extends React.Component {
// 有状态的组件需要使用render方法进行渲染,是生命周期里面非常基础和底层的方法,一定要用它来进行渲染
render() {
return <h1>hello weiyu</h1>
}
}
ReactDOM.render(
<Hello/>,
document.getElementById("root")
)
</script>
2.有状态组件props
<div id="root"></div>
<script type="text/babel">
class Hello extends React.Component {
render() {
return <div>
<h1>这是一个有状态组件</h1>
<h1>姓名:{this.props.name}</h1>
<h1>年龄:{this.props.age}</h1>
<h1>性别:{this.props.sex}</h1>
</div>
}
}
ReactDOM.render(
<Hello name="weiwei" age="18" sex="男"/>,
document.getElementById("root")
)
</script>
3.有状态组件state
<div id="root"></div>
<script type="text/babel">
class Hello extends React.Component {
// super是把属性传递给父级的构造类对象
// 子类必须在constructor构造器里调用super方法,从而得到父类的this对象,
// super()加不加props的区别究竟在哪里
// 需要在构造器函数中使用this.props则在super中添加props参数,
// 如果不需要传值的话就不用在super中接收props
constructor(props) {
super(props)
this.state = {
age: '18',
sex: '男'
}
console.log(props, this.props);
// this.props = props
}
render() {
return <div>
<h1>这是一个有状态组件</h1>
<h1>姓名:{this.props.name}</h1>
<h1>年龄:{this.state.age}</h1>
<h1>性别:{this.state.sex}</h1>
</div>
}
}
ReactDOM.render(
<Hello name="weiwei"/>,
document.getElementById("root")
)
</script>
4.state简写
<div id="root"></div>
<script type="text/babel">
class Hello extends React.Component {
// 在开发中比较倾向于下面这种写法
state = {
age: '16',
sex: '男'
}
render() {
return <div>
<h1>这是一个有状态组件</h1>
<h1>姓名:{this.props.name}</h1>
<h1>年龄:{this.state.age}</h1>
<h1>性别:{this.state.sex}</h1>
</div>
}
}
ReactDOM.render(
<Hello name="weiwei"/>,
document.getElementById("root")
)
// 怎么选择使用有状态组件还是无状态组件?我们开发中更多的应该去使用哪种组件?
// 更多时间尽可能的去选择使用无状态组件,因为如果是有状态组件,它就会触发生命周期所对应的一些函数
// 状态改变,就会触发虚拟dom重新渲染,它就会影响当前项目的运行,除非是需要对数据做一些存储、处理等,这个时候就选择有状态组件。
</script>
5.事件处理
<div id="root"></div>
<script type="text/babel">
class Hello extends React.Component {
constructor(props) {
super(props)
this.state = {
age: '16',
sex: '男',
flag: true
}
this.updateAge = this.updateAge.bind(this)
}
// 1、函数的作用域由函数调用的时候决定的,而不是函数声明的时候
// 如果使用函数的形式直接定义,要在constructor去绑定this改变this指向
updateAge() {
// console.log(this); // 如果不在constructor里面去绑定this打印出来的是undefined
this.setState({
age: 5
})
}
// 2、要么使用箭头函数赋值的方法来定义函数,这是工作中比较常用的方法
updateState = () => {
this.setState({
flag: !this.state.flag
})
}
render() {
return <div>
<h1>这是一个有状态组件</h1>
<h1>姓名:{this.props.name}</h1>
<h1>年龄:{this.state.age}</h1>
<h1>性别:{this.state.sex}</h1>
<button onClick={this.updateAge}>更新年龄</button>
<button onClick={this.updateState}>{this.state.flag ? 'YES' : 'NO'}</button>
</div>
}
}
ReactDOM.render(
<Hello name="weiwei"/>,
document.getElementById("root")
)
</script>
6.事件处理
<div id="root"></div>
<script type="text/babel">
class Hello extends React.Component {
state = {
name: 'jindu',
age: 14,
flag: true
}
updateInfo() {
console.log(this)
this.setState({
age: 5
})
}
updateState() {
this.setState({
flag: !this.state.flag
})
}
render() {
return <div>
<h1>这是一个有状态组件</h1>
<h1>姓名:{this.state.name}</h1>
<h1>年龄:{this.state.age}</h1>
<button onClick={this.updateInfo.bind(this)}>更新年龄</button>
<button onClick={() => this.updateState()}>{this.state.flag ? 'TRUE' : 'FALSE' }</button>
</div>
}
}
ReactDOM.render(
<Hello/>,
document.getElementById("root")
)
</script>
7.事件与条件处理
<div id="root"></div>
<script type="text/babel">
// 根据登录状态来展示不同的组件,true展示login组件,false展示logout组件
function Login() {
return <button>login</button>
}
function Logout() {
return <button>logout</button>
}
class Hello extends React.Component {
state = {
isLogin: true
}
updateState = () => {
this.setState({
isLogin: !this.state.isLogin
})
}
render() {
// const isLogin = this.state.isLogin
const { isLogin } = this.state
return <div>
<h1>这是一个有状态组件</h1>
{ isLogin ? <Login/> : <Logout/> }
<button onClick={this.updateState}>更新状态</button>
</div>
}
}
ReactDOM.render(
<Hello/>,
document.getElementById("root")
)
// React中为什么要使用setState修改状态?
// React没有实现类似于Vue2的Object.defineProperty或者是Vue3的proxy方式来监听数据的变化
// 所以必须通过setState来告知react状态的改变,setState是继承自Component,当我们调用setState的时候
// 会重新执行render方法
</script>
8.事件与条件处理
<div id="root"></div>
<script type="text/babel">
// 把父组件的方法传递给子组件,并且让子组件能够进行调用
function Login(props) {
// console.log(props);
return <button onClick={props.updated}>login</button>
}
function Logout(props) {
return <button onClick={props.updated}>logout</button>
}
class Hello extends React.Component {
state = {
isLogin: true
}
updateState = () => {
this.setState({
isLogin: !this.state.isLogin
})
}
render() {
const { isLogin } = this.state
return <div>
<h1>这是一个有状态组件</h1>
{ isLogin ? <Login/> : <Logout/> }
{ isLogin ? <Login updated={this.updateState}/>
: <Logout updated={this.updateState}/> }
<button onClick={this.updateState}>更新状态</button>
</div>
}
}
ReactDOM.render(
<Hello/>,
document.getElementById("root")
)
</script>
9.受限组件
<div id="root"></div>
<script type="text/babel">
// Vue事项了响应式数据,但是React中不具备响应式特性,所以在应用中有很多东西需要我们手动完成
class Hello extends React.Component {
render() {
return <input type="text" value="123" />
}
// 此时Hello作为一个受限组件渲染出来之后,我们去输入输入框任何值都无效,如果要响应用户输入的值,就必须使用onChange事件。
}
ReactDOM.render(
<Hello/>,
document.getElementById("root")
)
</script>
<!-- 双向数据绑定 -->
<hr>
<div id="root2"></div>
<script type="text/babel">
class App extends React.Component {
state = {
value: 'hello'
}
change = (event) => {
console.log(event);
this.setState({
value: event.target.value
})
}
render() {
return <div>
<input type="text" value={this.state.value} onChange={this.change} />
<h2>{this.state.value}</h2>
</div>
}
}
ReactDOM.render(
<App/>,
document.getElementById("root2")
)
</script>
<!-- radio、textarea、select -->
<hr>
<div id="root3"></div>
<script type="text/babel">
class Rts extends React.Component {
render() {
// 这些给了值之后都不能直接进行更改
return <div>
<input type="radio" checked />
<input type="checkbox" checked />
<input type="checkbox" checked />
<input type="checkbox" checked />
<select value="A">
<option value="A">A</option>
<option value="B">B</option>
<option value="C">C</option>
</select>
</div>
}
}
ReactDOM.render(
<Rts/>,
document.getElementById("root3")
)
</script>
总结
组件定义和事件处理
组件的分类
在React中,组件分为函数组件和class组件,也就是无状态组件和有状态组件。
- 函数式组件(无状态组件)
直接定义函数的形式,不存在state,只会有props,它没有生命周期函数。
// 函数式组件(无状态组件) function Hello() { return <h1>Hello,jianan</h1> } - class组件(有状态组件)
使用class定义、extends继承React.Component。有state进行数据的存储和管理,同时还可以拥有props,有生命周期函数。
// class组件(有状态组件) class Hello extends React.Component { render() { return <h1>Hello jianan</h1> } }
无状态组件和有状态组件的使用规则
1、因为数据的更改都是根据状态进行更改的,如果只是单纯的处理一些逻辑,而不需要改变数据的值就使用无状态组件。我们可以使用props进行组件间的通信和传值。 2、如果需要改变某些数据的话,或是想要存储一些数据并且想要对这些数据进行一些增删改查的话,那么就应该使用有状态组件。我们使用的是state,数据会发生变化就会触发生命周期这些函数。
无状态组件和有状态组件传值的取用
- 无状态组件 使用props传值时,使用props.属性名进行取用。
- 有状态组件 使用props传值时,使用this.props.属性名进行取用。使用state定义状态时,使用this.state.属性名进行取用。
一般数据会通过父组件进行传递,这样就可以进行统一的数据管理,在父级进行一个集中的数据管理。以上是在没有使用redux的情况下,如果使用了redux的话,就会在redux中进行状态管理。 思考:更多时候,我们应该使用无状态组件还是有状态组件?
事件处理
事件处理函数的相关语法
React元素的事件处理和DOM元素很相似,但是语法上有些不同,React事件的命名采用小驼峰(camelCase),而不是纯小写。使用JSX语法时需要传入一个函数作为事件处理函数,而不是一个字符串。
<button onclick='fn()'>点击事件</button>
// React里
<button onClick={fn}>点击事件</button>
另外在React中阻止默认事件不能通过返回false的形式,而是要使用preventDefault
<a href="#" onclick="console.log('hello');return false">click</a>
handleClick(e) {
e.preventDefault()
console.log('hello')
}
<a href="#" onClick={handleClick}>click</a>
事件处理函数中的this
在JS中,class方法默认不会绑定this,如果我们忘记绑定this.handleClick并且把它传入了onClick,当我们调用这个函数的时候this的值为undefined。
class Hello extends React.Component {
constructor() {
super()
// 如果不在constructor绑定this打印出来的值是undefined
this.updateInfo = this.updateInfo.bind(this)
}
updateInfo() {
console.log(this)
}
updateState() {
console.log(this)
}
updateAge() {
console.log(this)
}
render() {
return <div>
<button onClick={this.updateInfo}>更新信息</button>
{/* 像下面这样进行绑定也是可行的 */}
<button onClick={this.updateState.bind(this)}>更新状态</button>
<button onClick={() => this.updateAge()}>更新年龄</button>
</div>
}
}
或者使用下面这种用箭头函数赋值的方式来定义函数,这是在工作中最为常用的方法:
class Hello extends React.Component {
updateInfo = () => {
console.log(this)
}
render() {
return <button onClick={this.updateInfo}>更新信息</button>
}
}