一、React中的组件/模块、 组件化/模块化
- 对于组件,如下所示:
- 一个应用/版块/页面中用于实现某个局部的功能(包括
html, js, css
等) - 把这些局部功能组装到一起就形成了完整的一个大的功能
- 主要目的在于: 复用代码, 提高项目运行效率
-
对于组件化,如果一个应用是用多组件的方式进行综合开发的, 那么这个应用就是一个组件化应用。
-
对于模块,多个组件形成模块, 或者是一个提供特定功能的
js
文件, 主要特点在于耦合性低, 可移植性高, 执行效率好。 -
对于模块化,如果一个应用都是用模块的形式来构建的,那么这个应用就是模块化应用。
-
对于
React
中组件创建方式,有构造函数创建组件和class
关键字创建组件这两种。 -
对于构造函数创建组件,如下所示:
- 使用构造函数来创建组件时,如果要接收外界传递的数据,需要在构造函数的参数列表中使用
props
来接收; - 必须要向外
return
一个合法的JSX
创建的虚拟DOM
; - 简单组件、带参数组件和复合组件
- 代码如下:
<script type="text/babel">
function Name (props) {
return <p>{props.name}</p>
}
function Gender (props) {
return <p>{props.gender}</p>
}
function Likes (props) {
return <p>{props.likes}</p>
}
function Hello () {
return (
<div>
<Name name="张三"/>
<Gender gender="男"/>
<Likes likes={['react', 'vue']}/>
</div>
)
}
ReactDOM.render(<Hello/>, document.getElementById('app'))
</script>
- 对于
class
关键字创建组件,代码如下所示:
<script type="text/babel">
class Hello extends React.Component {
render () {
return (
<div>
<p>{props.name} --- {props.gender} --- {props.likes}</p>
</div>
)
}
}
ReactDOM.render(
<Hello name="张三" gender="男" likes={['react', 'vue']}/>,
document.getElementById('app')
)
</script>
- 对于构造函数创建组件和
class
关键字创建组件的区别,如下所示:
- 构造函数创建的组件叫:无状态组件;
class
关键字创建的组件叫:有状态组件;- 有状态组件与无状态组件的本质区别在于是否有
state
(状态)属性;
二、React中的state(状态)
state
,如下所示:
React
把组件看成是一个状态机(State Machines)
, 通过状态(State)
去操作状态机;- 在开发中, 通过与用户的交互,实现不同状态,然后渲染
UI
,让用户界面和数据保持一致; - 在
React
中,只需更新组件的state
,然后根据新的state
重新渲染用户界(不要操作DOM
)
- 对于
state
的使用方式,代码如下所示:
<script type="text/babel">
class Hello extends React.Component {
constructor (props) {
super(props)
this.state = {
name: '张三',
gender: "男",
age: 24
}
}
render () {
const {name, gender, age} = this.state
return (
<div>
<p>{props.name} --- {props.gender} --- {props.age}</p>
<button onClick={() => this.updateClick()}>换一个</button>
</div>
)
}
updateClick () {
this.setState({
name: '李四',
gender: "男",
age: 26
})
}
}
ReactDOM.render(
<Hello/>,
document.getElementById('app')
)
</script>
三、props和state混合使用
- 对于需求,如下所示:
- 分析
props
和state
的使用场景? props
的使用细节?- 单组件中, 两者的区别?
- 具体实现,代码如下所示:
<script type="text/babel">
class Person extends React.Component {
constructor (props) {
super(props)
// 初始化state
this.state = {
age: 24,
friends: []
}
}
// 设置props属性的默认值
static defaultProps = {
name: '张三',
gender: '男'
}
// 设置props属性的类型
static propTypes = {
name: PropTypes.string.required,
gender: PropTypes.string.isRequired
}
render () {
const {name, gender} = this.props
const {age, friends} = this.state
return (
<div>
<p>姓名:{name},性别:{gender}</p>
<p>年龄:{age}</p>
<p>我的朋友:</p>
<ul>
{
friends.map((friend, index) => (
<li key={index}>{friend}</li>
))
}
</ul>
<button onClick={() => this.addYear()}>加一</button>
</div>
)
}
addYear () {
// 增加
let tempArr = this.state.friends
tempArr.push('朋友'+Math.floor(Math.random()*100))
// 更新状态
this.setState({
age: this.state.age+1,
friends: tempArr
})
}
}
ReactDOM.render(
<Person />,
document.getElementById('app')
)
</script>
- 对于这个的总结,如下所示:
- 在单组件中,
props
一般用于接收外部传入的数据; 而state
则用于记录组件内部数据, 而且是需要经常改变的数据; state
是组件内部的状态(数据),不能够直接修改,必须要通过setState
来改变值的状态,从而达到更新组件内部数据的作用;props
更多是组件间传递数据的一种方式,props
同样也可以传递state
。由于React
的数据流是自上而下的,所以是从父组件向子组件进行传递;另外组件内部的this.props
属性是只读的不可修改;
四、ref的使用
-
对于
ref
,Refs
提供了一种方式,用于访问在render
方法中创建的DOM
节点或React
元素。 -
对于
ref
的使用场景,如下所示:
- 处理焦点、文本选择或媒体控制;
- 触发强制动画;
- 集成第三方
DOM
库;
-
注意,官方提示, 如果可以通过声明式实现,则尽量避免使用
refs
。话外音:React
无法控制局面的时候,就需要直接操作Refs
了。 -
具体实现,代码如下所示:
<script type="text/babel">
class CustomTextInput extends React.Component {
constructor (props) {
super(props)
// 绑定ref
this.myInput = React.createRef()
this.myBtn = React.createRef()
}
render () {
return (
<div>
<input ref={this.myInput} type="text" placeholder="请输入内容" />
<input ref={this.myBtn} type="button" value="获取焦点" onClick={() => this.focusTextInput()}/>
</div>
)
}
focusTextInput () {
// 获取焦点
this.myInput.current.focus()
}
}
ReactDOM.render(
<CustomTextInput />,
document.getElementById('app')
)
</script>
五、多组件的应用
- 多组件应用的删除和添加,具体实现,代码如下所示:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#app{margin: 50px auto;width: 600px;}
fieldset{border: 1px solid purple;margin-bottom: 20px;}
fieldset input{width: 200px;height: 30px;margin: 10px 0;}
table{width: 600px;border: 2px solid purple;text-align: center;}
thead{background-color: purple;color: #fff;}
</style>
</head>
<body>
<div id="app"></div>
<script src="../js/react.development.js"></script>
<script src="../js/react-dom.development.js"></script>
<script src="../js/babel.min.js"></script>
<script src="../js/prop-types.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
studentArr: [
{name: '周杰伦', age: 30, gender: '男', phone: '18899989839'},
{name: '谢霆锋', age: 40, gender: '男', phone: '18299839890'},
{name: '刘德华', age: 50, gender: '男', phone: '18998938783'}
]
}
this.delFromArrWithIndex = this.delFromArrWithIndex.bind(this)
}
delFromArrWithIndex (index) {
const { studentArr } = this.state
studentArr.splice(index, 1)
this.setState({
studentArr
})
}
render () {
return (
<div>
<Add />
<List studentArr={this.state.studentArr} delFromArr={this.delFromArrWithIndex}/>
</div>
)
}
}
class Add extends React.Component {
render () {
return (
<div>
<fieldset>
<legend>信息录入系统(React版)</legend>
<div>
<span>姓名: </span>
<input type="text" placeholder="请输入姓名"/>
</div>
<div>
<span>年龄: </span>
<input type="text" placeholder="请输入年龄"/>
</div>
<div>
<span>性别: </span>
<select>
<option value="男">男</option>
<option value="女">女</option>
</select>
</div>
<div>
<span>手机: </span>
<input type="text" placeholder="请输入手机号码"/>
</div>
<button>创建新用户</button>
</fieldset>
</div>
)
}
}
class List extends React.Component {
static propTypes = {
studentArr: PropTypes.array.isRequired,
delFromArr: PropTypes.func.isRequired
}
render () {
const { studentArr, delFromArr } = this.props
return (
<div>
<table>
<thead>
<tr>
<td>姓名</td>
<td>性别</td>
<td>年龄</td>
<td>手机</td>
<td>删除</td>
</tr>
</thead>
<tbody>
{
studentArr.map((student, index) => (
<tr key={index}>
<td>{student.name}</td>
<td>{student.gender}</td>
<td>{student.age}</td>
<td>{student.phone}</td>
<td>
<button onClick={() => this.delFromArr(index)}>删除</button>
</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render(
<App/>, document.getElementById('app')
)
</script>
</body>
</html>
-
多层组件中, 数据该放在何处?如果只用于一个组件, 则定义在该组件内容; 如果是运用于多个组件, 则定义在它们的父组件中。
-
多层组件中, 数据传递问题?父组件通过
props
传递数据给子组件, 子组件不能直接修改父组件的数据, 必须是父组件定义修改数据的函数, 传递给子组件然后由子组件调用。 -
受控方式的实现,代码如下所示:
<script type="text/babel">
class Login extends React.Component {
constructor (props) {
super(props)
this.state = {
userName: '',
userPwd: ''
}
}
render () {
const { userName, userPwd} = this.state
return (
<form action="" onSubmit={() => this.login()}>
<div>
<span>用户名:</span>
<input type="text" value={userName} onChange={(e) => this.userChange(e)} />
</div>
<div>
<span>密码:</span>
<input type="password" value={userPwd} onChange={(e) => this.pwdChange(e)} />
</div>
<input type="submit" value="登录" />
</form>
)
}
userChange (e) {
const userName = e.target.value
this.setState({
userName
})
}
pwdChange (e) {
const userPwd = e.target.value
this.setState({
userPwd
})
}
login () {
const { userPwd, userName } = this.state
alert(`用户名:${userName},密码:${userPwd}`)
return
}
}
ReactDOM.render(
<Login />,
document.getElementById('app')
)
</script>
- 在开发中尽可能用受控组件, 因为
ref
方式官方不希望过渡使用。