React
一.受控元素(组件)
一个标签(组件)受react中控制,受数据,受函数,等等(其实,就是一个标签(组件)里用了react里的东西)
表单的value受控,受数据控制,
<input type="text" value={this.state.数据名} /> //model 控制 view。
<input type="text" onChange={this.方法} /> //view 控制 model
1.1双向绑定
class Books extends React.Component {
constructor(props) {
super();
this.state = {
username: "",
sex: "女",
hobby: ["打羽毛球"],
profession: "计算机科学与技术",
};
}
// 文本框: 修改用户名
changeName = (ev) => {
this.setState({
username: ev.target.value,
});
};
// 单选按钮
changeSex = (ev) => {
this.setState({
sex: ev.target.value,
});
};
// 复选框
changeHobby = (ev) => {
let arr;
if (ev.target.checked) {
// 当选中复选框时,则将选中的数据添加到数组中
// 添加到数组中
arr = [...this.state.hobby, ev.target.value];
} else {
// 删除
let index = this.state.hobby.indexOf(ev.target.value);
arr = this.state.hobby;
arr.splice(index, 1);
}
this.setState({
hobby: arr,
});
};
// 下拉框
changeProfession = (ev) => {
this.setState({
profession: ev.target.value,
});
};
render() {
return (
<div>
{/*react中的onChange事件就是在用户输入的同时,就会被触发,相当于原生中的input*/}
用户名:
<input
type="text"
value={this.state.username}
onChange={this.changeName}
/>
<br />
性别:
<label htmlFor="nv">
<input
type="radio"
name="sex"
id="nv"
value="女"
checked={this.state.sex == "女"}
onChange={this.changeSex}
/>
女
</label>
<label htmlFor="nan">
<input
type="radio"
name="sex"
id="nan"
value="男"
checked={this.state.sex == "男"}
onChange={this.changeSex}
/>
男
</label>
<br />
请选择你的爱好: <br />
<input
type="checkbox"
value="singing"
checked={this.state.hobby.indexOf("singing") != -1}
onChange={this.changeHobby}
/>
singing
<input
type="checkbox"
value="打羽毛球"
checked={this.state.hobby.indexOf("打羽毛球") != -1}
onChange={this.changeHobby}
/>
打羽毛球
<input
type="checkbox"
value="舞蹈"
checked={this.state.hobby.indexOf("舞蹈") != -1}
onChange={this.changeHobby}
/>
舞蹈
<input
type="checkbox"
value="篮球"
checked={this.state.hobby.indexOf("篮球") != -1}
onChange={this.changeHobby}
/>
篮球
<br />
请选择您的专业:
<select
value={this.state.profession}
onChange={this.changeProfession}
>
<option value="网络工程">网络工程</option>
<option value="计算机科学与技术">计算机科学与技术</option>
<option value="数学与应用数学">数学与应用数学</option>
</select>
</div>
);
}
}
ReactDOM.render(
<div>
<Books />
</div>,
document.getElementById("app")
);
1.2 处理多个输入元素(双向绑定的封装)
可以为每个元素添加一个 name 属性(通常和数据名一致),处理函数根据 event.target.name 的值来选择要做什么
class Books extends React.Component {
constructor(props) {
super();
this.state = {
username: "",
sex: "女",
hobby: ["打羽毛球"],
profession: "计算机科学与技术",
};
}
// 封装文本框,单选框,下拉框的双向绑定
changeVal=(ev)=>{
this.setState({
[ev.target.name]:ev.target.value
})
}
render() {
return (
<div>
用户名:
<input
type="text" name="username"
value={this.state.username}
onChange={this.changeVal}
/>
<br />
性别:
<label htmlFor="nv">
<input
type="radio"
name="sex"
id="nv"
value="女"
checked={this.state.sex == "女"}
onChange={this.changeVal}
/>
女
</label>
<label htmlFor="nan">
<input
type="radio"
name="sex"
id="nan"
value="男"
checked={this.state.sex == "男"}
onChange={this.changeVal}
/>
男
</label>
<br />
请选择您的专业:
<select
value={this.state.profession}
onChange={this.changeVal}
name="profession"
>
<option value="网络工程">网络工程</option>
<option value="计算机科学与技术">计算机科学与技术</option>
<option value="数学与应用数学">数学与应用数学</option>
</select><br />
</div>
);
}
}
ReactDOM.render(
<div>
<Books />
</div>,
document.getElementById("app")
);
二. 非受控元素(组件)
要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据
<input type="text" value="哈哈" ref="xx" />
2.1默认值
表单元素上的 value
将会覆盖 DOM 节点中的值,在非受控组件中,你经常希望 React 能赋予组件一个初始值,但是不去控制后续的更新,指定一个 defaultValue
属性,而不是 value
2.2可选案例:增删改查
- 增
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
table {
width: 800px;
margin: 20px auto;
background-color: #ccc;
border-collapse: collapse;
}
th {
background-color: green;
}
tr {
text-align: center;
height: 30px;
}
th,
td {
vertical-align: middle;
}
input[type="button"] {
outline: none;
margin-right: 5px;
}
.addBox {
position: fixed;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
width: 300px;
height: 200px;
background-color: #f5f5f5;
border: 1px solid #999;
padding: 10px 20px;
}
.addBox input {
margin-bottom: 10px;
outline: none;
}
</style>
</head>
<body>
<div id="app"></div>
</body>
<script src="./js/react.js"></script>
<script src="./js/react-dom.js"></script>
<script src="./js/babel.min.js"></script>
<script src="./js/prop-types.js"></script>
<script type="text/babel">
// 学生管理系统
class Students extends React.Component {
constructor(props) {
super();
this.state = {
isShowAddBox: "none",
users: [
{
id: "01001",
username: "张三疯",
profession: "计算机科学与技术",
faculty: "数学与计算机应用学院",
classes: "1803班",
},
{
id: "01002",
username: "李四",
profession: "统计专业",
faculty: "数计院",
classes: "1702班",
},
{
id: "01003",
username: "王五",
profession: "思政",
faculty: "马克思主义学院",
classes: "1603班",
},
{
id: "01004",
username: "语文",
profession: "电科",
faculty: "电信院",
classes: "1603班",
},
],
newUser: {
id: "",
username: "",
profession: "",
faculty: "",
classes: "",
},
};
}
// 显示添加窗口
showAddBox = () => {
this.setState({
isShowAddBox: this.state.isShowAddBox == "block" ? "none" : "block",
});
};
//添加里面的数据实现双向绑定
changeVal = (ev) => {
let obj = { ...this.state.newUser, [ev.target.name]: ev.target.value };
this.setState({
newUser: obj,
});
};
// 保存按钮,将表单中的数据添加到表格中,并清空内容
addSave = () => {
this.state.users.push(this.state.newUser);
this.setState({
isShowAddBox: "none",
newUser: {
id: "",
username: "",
profession: "",
faculty: "",
classes: "",
},
});
};
render() {
return (
<div>
<input type="button" value="添加" onClick={this.showAddBox} />
<table border="1">
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>学院</th>
<th>专业</th>
<th>班级</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{this.state.users.map((user, index) => (
<tr key={index}>
<td>{user.id}</td>
<td>{user.username}</td>
<td>{user.faculty}</td>
<td>{user.profession}</td>
<td>{user.classes}</td>
<td>
<input type="button" value="修改" />
<input type="button" value="删除" />
</td>
</tr>
))}
</tbody>
</table>
<div
className="addBox"
style={{ display: this.state.isShowAddBox }}
>
学号:
<input
type="text"
value={this.state.newUser.id}
name="id"
onChange={this.changeVal}
/>
<br />
姓名:
<input type="text"
value={this.state.newUser.username}
name="username"
onChange={this.changeVal}/>
<br />
学院:
<input type="text"
value={this.state.newUser.faculty}
name="faculty"
onChange={this.changeVal}/>
<br />
专业:
<input type="text"
value={this.state.newUser.profession}
name="profession"
onChange={this.changeVal}/>
<br />
班级:
<input type="text"
value={this.state.newUser.classes}
name="classes"
onChange={this.changeVal}/>
<br />
<input type="button" value="保存" onClick={this.addSave} />
<input type="button" value="取消" onClick={this.showAddBox}/>
</div>
</div>
);
}
}
ReactDOM.render(
<div>
<Students></Students>
</div>,
document.getElementById("app")
);
</script>
</html>
小括号错写成大括号,找了好几个小时,还是换人换马~
三.生命周期及其钩子函数
react组件生命周期经历的阶段:初始化阶段 -----> 运行阶段(更新期)-----> 销毁阶段
3 .1 初始化阶段 (挂载):
(在这个阶段完成了vue中数据挂载和模板渲染)
组件实例被创建并插入 DOM 中时,其生命周期钩子函数的调用顺序如下:
1)、constructor:构造函数里,可以做状态的初始化,接收props的传值
2)、componentWillMount: 在渲染前调用,相当于vue中的beforeMount
3)、render:渲染函数,不要在这里修改数据。 vue中也有render函数。
4)、componentDidMount :相当于vue中的 mounted
渲染完毕,在第一次渲染后调用。之后组件已经生成了对应的DOM结构, 如果你想和其他JavaScript框架(swiper)一起使用,可以在这个方法中使用,包括调用setTimeout, setInterval或者发送AJAX请求等操作,相当于vue的mounted
class Students extends React.Component {
constructor(props) {
super();
this.state = {
name: "皮卡丘",
};
console.log("constructor");
}
componentWillMount() {
console.log("componentWillMount");
}
componentDidMount() {
console.log("componentDidMount");
}
render() {
console.log("render");
return <div></div>;
}
}
ReactDOM.render(
<div>
<Students></Students>
</div>,
document.getElementById("app")
);
3.2运行中阶段(更新)
(相当于vue中更新阶段)
当组件的 props 或 state 发生变化时会触发更新(严谨的说,是只要调用了setState()或者改变了props时)。组件更新的生命周期调用顺序如下:
1)、shouldComponentUpdate(nextProps, nextState) 是否更新? 需要返回true或者false。如果是false,那么组件就不会继续更新了。
2)、componentWillUpdate,即将更新。相当于vue中的 beforeUpdate
3)、 componentWillReceiveProps(nextProps): 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。nextProps 是props的新值,而 this.props是旧值。
4)、render
不要在这里修改数据
5)、componentDidUpdate
在组件完成更新后立即调用。在初始化时不会被调用。 相当于vue中的updated
3.3 销毁阶段(卸载)
componentWillUnmount()
即将卸载,可以做一些组件相关的清理工作,例如取消计时器、网络请求等。
注意:
父更新则子更新,子更新父不更新
class Parent extends React.Component {
constructor(props) {
super();
this.state = {
name: "张三疯",
};
console.log("Parent======constructor");
}
// 首次渲染前
componentWillMount() {
console.log("Parent======componentWillMount");
}
// 首次渲染后
componentDidMount() {
console.log("Parent======componentDidMount");
}
// 组件更新前
componentWillUpdate() {
console.log("Parent======componentWillUpdate");
}
// 组件更新后
componentDidUpdate() {
console.log("Parent======componentDidUpdate");
}
changeName = () => {
this.setState({
name: "hahhah",
});
};
render() {
console.log("Parent======render");
return (
<div>
<h1>父组件</h1>
<p>{this.state.name}</p>
<input type="button" value="父组件修改姓名" onClick={this.changeName} />
<Son />
</div>
);
}
}
class Son extends React.Component {
constructor(props) {
super();
console.log("Son======constructor");
}
// 首次渲染前
componentWillMount() {
console.log("Son======componentWillMount");
}
// 首次渲染后
componentDidMount() {
console.log("Son======componentDidMount");
}
// 组件更新前
componentWillUpdate() {
console.log("Son======componentWillUpdate");
}
// 组件更新后
componentDidUpdate() {
console.log("Son======componentDidUpdate");
}
changeName = () => {
this.setState({
});
};
render() {
console.log("Son======render");
return <div>
<input type="button" value="子组件修改姓名" onClick={this.changeName} /></div>;
}
}
ReactDOM.render(
<div>
<Parent />
</div>,
document.getElementById("app")
);
在这里插入图片描述
在这里插入图片描述
react17,不建议使用的钩子函数(面试过程中有问到):
UNSAFE_componentWillMount 不建议用,可以会出现bug,不能初始化因为会受到React16.xFiber的协调算法,函数会执行多次,如果把异步请求放到该钩子函数中,异步请求可能也会执行多次。
UNSAFE_componentWillReceiveProps
UNSAFE_componentWillUpdate
今天的blog写的有点不满意。。。