文章目录
初识
react是一个将数据渲染为HTML视图的开源JavaScript库
React的优点
- 采用组件化模式,声明式编码,提高开发效率及组件复用率
- 在React Native中可以使用React语法进行移动端开发(React Native可以编写苹果安卓应用)
- 使用虚拟dom+优秀的Diffing算法,尽量减少与真实DOM的交互,最小化页面重绘
Hello React
babel.js
用来转换js和jsx
可以通过 CDN 获得 React 和 ReactDOM 的 UMD 版本。
引入顺序要主意
引入核心库
dom
babel
<!-- 准白好一个容器 -->
<div id="test"></div>
<!-- 引入核心库 -->
<script src="../react.development.js"></script>
<!-- 引入react-dom用于支持react操作DOM -->
<script src="../react-dom.development.js"></script>
<script src="../babel.min.js"></script>
<script type="text/babel"> //此处必须写成Babel
//创建虚拟dom
const Virtual = ( //此处一定不要写引号 因为是虚拟dom
<h1>hello,react</h1>
)
//渲染虚拟dom到页面
// ReactDOM.render(虚拟DOM,容器)
//ReactDOM.render(Virtual,"#test") //react没有提供这样的写法
ReactDOM.render(Virtual,document.getElementById('test'));
</script>
js和jsx 区别
使用js来创建虚拟dom
const VDOM = React.createElement("h1, {id:"title"}, React.createElement("span"));
使用jsx 会自动转换成上面的样子 其实就是语法糖
const Virtual = (
<h1>
<span id="title"></span>
</h1>
)
虚拟dom 注释
const Virtual = (
<h1>
{ /* <span id="title"></span> */ }
{变为代码块} {/* 代码块里就放个注释 */}
</h1>
)
const Virtual = (
<h1>
<span id="title"></span>
</h1>
)
虚拟dom Virtual就是一个对象
debuger测试看来说
虚拟dom比较轻 因为很多用不上
jsx语法规则javascript xml
jsx本质是语法糖const VDOM = React.createElement("h1, {id:“title”},React.createElement(“span”));
标签混入js表达式时要用{} -----何为表达式?何为语句?
const myId = 'aTguiGu'
const myData = 'hello,ReaCt'
const Virtual = (
<h1 id={myId.toLocaleLowerCase()}> //标签混入js表达式时要用{}
<span>{myData.toLocaleLowerCase()}</span>
</h1>
)
ReactDOM.render(Virtual,document.getElementById('test'));
表达式
a
a+b
demo(1)
arr.map()
function(){}
const i = ?能接到值就是表达式
语句
if(){}
for(){}
switch(){}
const i = 接不到值
jsx中样式class要写className 因为和js中class类冲突
const Virtual = ( //此处一定不要写引号 因为是虚拟dom
<h1 className="title" id={myId.toLocaleLowerCase()}>
<span>{myData.toLocaleLowerCase()}</span>
</h1>
)
内联样式需要双括号 小驼峰 sytle={{key:value,key:value}}
const Virtual = ( //此处一定不要写引号 因为是虚拟dom
<h1 className="title" id={myId.toLocaleLowerCase()}>
<span style={{color:'red',fontSize:'50px'}}>{myData.toLocaleLowerCase()}</span>
</h1>
)
只能有一个根标签 标签必须闭合
标签首字母
若小写字母开头,则将标签转为html中同名元素,若html没有对应元素 则报错
若大写字母开头,直接找对应组件
小练习 渲染数组的值 react会自动遍历数组
react会自动帮你遍历数组
const data = ['Angular','React','Vue']
const Virtual = (
<div>
<h1>前端框架</h1>
<ul>
{
data.map((item,index)=>{ //只能写表达式
return <li key={index}>{item}</li>
})
}
</ul>
</div>
)
ReactDOM.render(Virtual,document.getElementById('test'));
面向组件编程
谷歌网上商店下载安装react dev tools
控制台就可以看组件和测试工具
定义组件的方法
函数式组件
函数必须有返回值
挂在必须为结束标签
// 创建函数式组件
function MyComponent() {
return <h2>我是函数定义组件(适用于【简单组件】的定义)</h2> //必须有返回值
}
//必须为结束标签
ReactDOM.render(<MyComponent/>,document.getElementById('test'));
class类的使用
//创造一个Person类
class Parson{
//构造器
constructor(name,age){
this.name = name
this.age = age
}
//一般方法
speak(){
//speak写在原形链上面
console.log(`我叫${this.name},我今年${this.age}岁`);
}
}
//创建一个学生类 继承parson
class Student extends Parson{
//没有构造器 直接搬过来继承的构造器
}
const s1 = new Student("小张",12)
console.log(s1);
如果继承以后有新的属性要用构造器
//创造一个Person类
class Parson{
//构造器
constructor(name,age){
this.name = name
this.age = age
}
//一般方法
speak(){
//speak写在原形链上面
console.log(`我叫${this.name},我今年${this.age}岁`);
}
}
//创建一个学生类 继承parson
class Student extends Parson{
constructor(name,age,grade){
super(name,age) //super只能写到第一行
this.grade = grade
}
}
const s1 = new Student("小张",12,"高一") //比parson多了一个年级
console.log(s1);
类中的构造器不是必须的 不写也可以
类中的方法都放到了原型对象上
类式组件
类必须继承React内置类
必须有render方法
render必须有返回值
// 创建类式组件
class MyComponent extends React.Component{ //必须继承
render(){ //render方法
return <h2>我是类定义组件(适用于【复杂组件】的定义)</h2> //返回值
}
}
//渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
/*
class MyComponent类里面的render是供实例使用
但是并没有new一个实例
当渲染的时候 react会自动new一个实例 并调用render方法
*/
// 创建类式组件
class MyComponent extends React.Component{
render(){
// render放在那?--MyComponent的原形对象上,供实例使用
// rander中的this是谁?--MyComponent的实例对象 或组件实例对象
console.log(this);
return <h2>我是类定义组件(适用于【复杂组件】的定义)</h2>
}
}
//渲染组件到页面
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
$state-组件实例的核心属性- 对state(状态)的理解
函数式组件=简单组件=无state
类式组件=复杂组件=有state
解决changeWeather中this指向问题
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:true}
}
render(){
//读取状态
const {isHot} = this.state
//这里必须写this.changeWeather因为直接写拿不到
return <h1 onClick={this.changeWeather.bind(this)}>今天天气很{isHot?"炎热":"寒冷"}</h1>
}
changeWeather(){
//局部会自动开启严格模式 所以上面不写bind的话this指向undefined
console.log(this.state);
}
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
或者以下
构造器里的this就是实例对象
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:true}
this.changeWeather = this.changeWeather.bind(this) //构造器里的this就是实例对象 这里挂上 点击输出的就是自身的changeWeather
}
render(){
//读取状态
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
}
changeWeather(){
console.log(this.state);
}
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
setState的使用 类似小程序
通过上面解决了指向问题 现在要点击变换true和false
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:true}
this.changeWeather = this.changeWeather.bind(this)
}
render(){
//读取状态
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
}
changeWeather(){
//这里确实改了 但是页面不会炎热寒冷一直切换
this.state.isHot = !this.state.isHot
console.log(this.state.isHot);
}
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
因为状态里的东西不能直接更改,找到那个属性直接改就是直接更改
class Weather extends React.Component{
constructor(props){
super(props)
//初始化状态
this.state = {isHot:true}
this.changeWeather = this.changeWeather.bind(this)
}
render(){
//读取状态
const {isHot} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
}
changeWeather(){
//严重注意state状态不可直接更改
// this.state.isHot = !this.state.isHot //错误写法
// console.log(this.state.isHot);
//由于上面写了this.changeWeather = this.changeWeather.bind(this)
//则这里面的this就是Weather的实例对象
//实例对象的原型上面有render 有changeWeather 原型的原型是React.Component 里面有个方法叫setState
this.setState({
isHot:!this.state.isHot
})
}
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
整体结构的小总结 精简方式
小总结
class Weather extends React.Component{
constructor(props){ //只会调用一次 生成实例的时候
super(props)
}
render(){} //1+n次 先调用把组件return出去 然后当事件函数先触发修改状态 重新渲染则在调用
changeWeather(){} //触发几次执行几次
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
class Weather extends React.Component{
constructor(props){ //构造器为啥要写?
super(props)
//必须初始化状态
this.state = {
isHot:true,
wind:"微风"
}
//解决this指向问题
this.changeWeather = this.changeWeather.bind(this)
}
render(){ //render里的this就是组件实例对象
//读取状态
const {isHot,wind} = this.state
//做展示
return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"}</h1>
}
changeWeather(){
this.setState({
isHot:!this.state.isHot //获取值 更新值
})
}
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
精简state
class Weather extends React.Component{
constructor(props){} //this肯定指向实例对象
render(){} //react内部new出来实例对象 会自动调用render所以this也是实例对象
changeWeather(){} // this是undefined 内部自动严格模式
}
constructor里面只有两件事 解决this指向 初始化状态
类里面可以直接写赋值语句 但是必须死值
class Weather extends React.Component{
constructor(props){}
render(){}
changeWeather(){}
a = 1
}
new出来的实例就会有a=1的属性 等价于在constructor里面写了
this.a = 1
所以直接精简state
class Weather extends React.Component{
constructor(props){}
state = {name:"xxx"}
render(){}
changeWeather(){}
}
精简constructord的this指向问题
class Weather extends React.Component{
constructor(props){}
render(){}
changeWeather = function(){} //这样写就是赋值语句 和a=1一个意思
//本来这个方法是在原型上 这样就直接到了实例的属性上
//光这样写还是不行 仅仅是换了个地方 原型到自身而已
}
class Weather extends React.Component{
constructor(props){}
render(){}
//箭头会拿外部的this
log(this)?? //类里面不能写函数体 所以不必纠结 既然箭头拿外部this 直接箭头里输出this
changeWeather = ()=>{
console.log(this) //为weather的实例对象
}
}
最终结构 注意要点
class Weather extends React.Component{
//初始化状态
state = {
isHot:true,
wind:"寒冷"
}
render(){
const {isHot,wind} = this.state
return <h1 onClick={this.changeWeather}>今天天气很{isHot?"炎热":"寒冷"},{wind}</h1>
}
//自定义方法--赋值语句+箭头函数
changeWeather = ()=>{
this.setState({
isHot:!this.state.isHot
})
}
}
ReactDOM.render(<Weather/>,document.getElementById("test"))
注意要点
- 组件中render的this为组件实例对象 (因为react自动new并调用)
- 组件自定义方法中this为undefined(因为类中自动为严格模式) 并且事件调用时没有this
a. 强制绑定this 通过bind() (bind不同于call不会执行)
b.通过箭头函数 - 状态数据不能直接修改 必须拿到实例原型的原型React里面的setState来改
$props-组件实例的核心属性- props
基本使用 简单传值
//创建组件
class Parson extends React.Component{
state = { name:'tom', age:18, sex:'女' }
render(){
const {name,age,sex} = this.state
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Parson/>,document.getElementById('app1'))
ReactDOM.render(<Parson/>,document.getElementById('app2'))
ReactDOM.render(<Parson/>,document.getElementById('app3'))
这是基本写法,但是现在 我需要从外部拿到数据 来渲染 上面这种写法像是自家的事情
而且渲染的三个人都是一个名字性别年龄
//创建组件
class Parson extends React.Component{
render(){
console.log(this);
const {name,age,sex} = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
ReactDOM.render(<Parson name='tom' age='18' sex='女' />,document.getElementById('app1'))
//这里react会自动把name:tom传到props里面
批量传递props 比如信息很多
批量传递props
批量传递标签属性
//创建组件
class Parson extends React.Component {
render() {
console.log(this);
const { name, age, sex } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
const result = { name: "老刘", age: 18, sex: "女" }
ReactDOM.render(<Parson name={result.name} age={result.age} sex={result.sex} />, document.getElementById('app1'))
//nonono如果数据太多 则不能这样写
//这样写
ReactDOM.render(<Parson {...result} />, document.getElementById('app2'))
测试。。。
var a = {n: 1, b: 2, c: "你好"}
var b = {say:"hello"}
var obj = {...b,...a} 或者 var obj = new Object({...b,...a})
obj的值为{say: "hello", n: 1, b: 2, c: "你好"}
对props进行限制propTypes
限制必须写到类上面 而不是实例上面
//创建组件
class Parson extends React.Component {
render() {
console.log(this);
const { name, age, sex } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
//固定写法 react每次new都会去问Parson类身上有没有这个东西
Parson.propTypes = {
//开头大写的P,这是react的内置属性
name:React.propTypes.string //类型也为小写
}
const result = { name: "老刘", age: 18, sex: "女" }
ReactDOM.render(<Parson name={result.name} age={result.age} sex={result.sex} />, document.getElementById('app1'))
ReactDOM.render(<Parson {...result} />, document.getElementById('app2'))
限制规则
Parson.propTypes = {
//开头大写的P,这是react的内置属性
name:React.propTypes.string //类型也为小写
}
但是这种写法在16.xx后被弃用 因为react上带着propTypes会变的很大
而且有时候也不一定非要进行限制
16.xx以后 就是新弄一个一个依赖包来进行 需要就载入prop-types.js
用来对组件标签进行限制 引入则多了一个对象 PropTypes
直接在类.PropTypes里写
//固定写法 react每次new都会去问Parson类身上有没有这个东西
Parson.propTypes = {
//开头大写的P,这是react的内置属性
name:PropTypes.string.isRequired, //类型也为小写 必须填写
sex:propTypes.string,
}
默认值
//指定默认值
Parson.defaultProps = {
sex:"不男不女",
age:18
}
如果穿的是方法 则限制改为handel:PropTypes.func
//创建组件
class Parson extends React.Component {
render() {
const { name, age, sex,handel } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
}
//固定写法 react每次new都会去问Parson类身上有没有这个东西
Parson.propTypes = {
//开头大写的P,这是react的内置属性
name:PropTypes.string.isRequired, //类型也为小写 必须填写
sex:PropTypes.string,
age:PropTypes.number,
handel:PropTypes.func //传方法
}
//指定默认值
Parson.defaultProps = {
sex:"不男不女",
age:18,
}
function handle(){}
const result = { name: "老刘",sex:"男", age: 18,handle:handle}
ReactDOM.render(<Parson {...result} />, document.getElementById('app2'))
propTypes的简写方式 props只读不能改
props是只读的不能this.props等于xxx
render() {
const { name, age, sex,handel } = this.props
this.prop.xxx = xxx //报错
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
简写
class Parson extends React.Component {
render() {
const { name } = this.props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
static propTypes = { //直接写进去是写到了实例对象上 并不是类上面 必须在前面加static静态的
name: PropTypes.string.isRequired
}
static defaultProps = {
sex: "不男不女"
}
}
Parson.propType = { //如果把这个直接写进去 得加static
name: PropTypes.string.isRequired,
}
类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。
class Foo {
static say() {
return 'hello';
}
}
Foo.say() // 'hello'
var foo = new Foo();
foo.say() // TypeError: foo.classMethod is not a function
注意,如果静态方法包含this关键字,这个this指的是类,而不是实例。
类式组件中的构造器与props
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。
constructor(props){
super() //这里不传的话
console.log(props);
console.log(this.props); //那么这里就有可能拿不到 直接为undefined
}
函数式组件的props
函数因为可以拿参数所以可以有props
state只能后面学到hooks
refs不可以
function Parson(props) {
const { name, sex, age, } = props
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{age}</li>
<li>年龄:{sex}</li>
</ul>
)
}
Parson.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
handel: PropTypes.func
}
Parson.defaultProps = {
sex: "不男不女",
age: 18,
}
function handle() { }
const result = { name: "老刘", age: 18, handle: handle }
ReactDOM.render(<Parson {...result} />, document.getElementById('app1'))
props总结
//组件内部不要修改props的值 为只读
//可以使用{...obj}来批量传递
ReactDOM.render(<Parson {...result} />, document.getElementById('app1'))
//进行props限制
类名.defaultProps = {
sex: "不男不女",
age: 18,
}
或者写在类里面加static
//函数式组件也可以用props
$refs-组件实例的核心属性 与 事件处理
字符串形式的ref-已不被推荐使用16.8还能用
首先有一个需求
两个input框
第一个输入内容点击按钮则弹出显示内容
第二个输入内容失去焦点则弹出显示内容
//基本写法
class Parson extends React.Component {
render() {
return (
<div> //这里加了id
<input id="i1" type="text" placeholder="点击按钮输出数据" />
<button onClick={this.btn}>点我弹出左侧数据</button>
<input type="text" placeholder="失去焦点输出数据" />
</div>
)
}
btn = () => {
const i = document.getElementById("i1") //可是都用了react没必要这样写
alert(i.value)
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
//使用react里的ref
class Parson extends React.Component {
render() {
return (
<div> //使用ref
<input ref="input1" type="text" placeholder="点击按钮输出数据" />
<button onClick={this.btn1}>点我弹出左侧数据</button>
<input ref="input2" onBlur={this.btn2} type="text" placeholder="失去焦点输出数据" />
</div>
)
}
btn1 = () => {
console.log(this.refs.input1.value) //这里用refs
}
btn2 = () => {
console.log(this.refs.input2.value)
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
回调形式的ref 调用次数的小问题
标签必须用ref关键字
class Parson extends React.Component {
render() {
return (
<div> //往外找this 找到render 找到实例
<input ref={c => this.input1 = c} type="text" placeholder="点击按钮输出数据" />
<button onClick={this.btn1}>点我弹出左侧数据</button>
<input ref={(c) => {this.input2 = c}} onBlur={this.btn2} type="text" placeholder="失去焦点输出数据" />
</div>
)
}
btn1 = () => {
console.log(this.input1.value);
}
btn2 = () => {
console.log(this.input2.value);
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
回调形式直接写回调函数 this指向recder指向实例
调用次数的小问题
回调函数调用次数 问题 当写为回调函数 且状态被修改后 react会再次调用render 回调函数会重置为null在赋值为你写的函数
当代码如下时
class Parson extends React.Component {
state = {
isHot: true
}
render() {
const { isHot } = this
return (
<div>
<input ref={c => { this.input1 = c; console.log("输出了", c); }} type="text" />
<button onClick={this.btn1}>点我弹出左侧数据</button>
<h1 onClick={this.handleH1}>今天天气{isHot ? "很热" : "寒冷"}</h1>
</div>
)
}
btn1 = () => {
console.log(this.input1.value);
}
handleH1 = () => {
this.setState({
isHot:!this.isHot
});
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
log会在页面生成的时候输出一句输出了
因为react默认执行一次render
当点击和标签时
输出结果如下:
输出了 null
Inline Babel script:11 输出了
因为当状态改变reder重新运行 会初始化为null 在运行函数
解决方案
挂载到自身的函数中 class中的绑定函数
就可以避免 但是大多数情况下 是无关紧要的
class Parson extends React.Component {
state = {
isHot: true
}
render() {
const { isHot } = this
return (
<div>
<input ref={this.saveInput} type="text" />
<button onClick={this.btn1}>点我弹出左侧数据</button>
<h1 onClick={this.handleH1}>今天天气{this.state.isHot ? "很热" : "寒冷"}</h1>
</div>
)
}
saveInput = (c)=>{
this.input1 = c
console.log("输出",c);
}
btn1 = () => {
console.log(this.input1.value);
}
handleH1 = () => {
this.setState({
isHot:!this.state.isHot
});
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
createRef的使用
class Parson extends React.Component {
myRef1 = React.createRef() //调用后可以返回一个容器 该容器可以存储被ref标识的节点
//等于我弄了个容器挂在的实例自身 起名为myRef
//但是该容器 专人专用 里面只能存一个
myRef2 = React.createRef()
state = {
isHot: true
}
render() {
const { isHot } = this
return (
<div>
<input ref={this.myRef1} type="text" />
{/*react发现你放的是React.createRef()创造的容器 就把当前的节点存到容器里*/}
<input ref={this.myRef2} onBlur={this.handle2} type="text" />
<button onClick={this.handle1}>点我弹出左侧数据</button>
</div>
)
}
handle1 = () => {
console.log(this.myRef1.current); //这里的current是react给你生成的
}
handle2 = () => {
console.log(this.myRef2.current);
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
例子2
class Parson extends React.Component {
myRef = {
myRef1:React.createRef(),
myRef2:React.createRef()
}
state = {
isHot: true
}
render() {
const { isHot } = this.state
//这里尝试结构结果不行
//只能一个一个
const {myRef1,myRef2} = this.myRef
return (
<div>
<input ref={myRef1} type="text" />
{/*react发现你放的是React.createRef()创造的容器 就把当前的节点存到容器里*/}
<input ref={myRef2} onBlur={this.handle2} type="text" />
<button onClick={this.handle1}>点我弹出左侧数据</button>
</div>
)
}
handle1 = () => {
console.log(this.myRef.myRef1.current); //这里的current是react给你生成的
}
handle2 = () => {
console.log(this.myRef.myRef2.current);
}
}
ReactDOM.render(<Parson />, document.getElementById('app1'))
事件处理 可以用target的时候少用refs
- 通过onXxx属性指定事件处理函数(注意大小写)
a. React使用的是自定义(合成)事件,而不是使用的元素DOM事件 – 为了更好的兼容性封装过
b. React中的事件是通过事件委托方式处理的(委托给组件最外层的元素) – 为了高效 - 通过event.target得到发生事件的DOM元素对象
收集表单数据
受控 非受控组件
受控组件
现用现取
输入类的dom随着你的输入维护到state里 需要的时候直接从状态里取
class Login extends React.Component {
state = {
username:'',
password:''
}
render() {
return (
<div>
<form action="" onSubmit={this.handleSubmit}>
用户名<input onChange={this.saveUsername} type="text" name="username" />
密码<input onChange={this.savePassword} type="password" name="password" />
<button>登陆</button>
</form>
</div>
)
}
saveUsername = (e) => {
this.setState({
username:e.target.value
})
}
savePassword = (e) => {
this.setState({
password:e.target.value
})
}
handleSubmit = (e) => {
//这里打印完会转跳到form里面的action的地址
e.preventDefault() //阻止默认事件 阻止表单提交
const {username,password} = this.state
console.log(username,password);
}
}
ReactDOM.render(<Login />, document.getElementById('app1'))
非受控
用ref取到节点当前的值
很多的使用柯里化来写
如果我想实现登陆可以这样写
class Login extends React.Component {
state = {
username:'',
password:''
}
render() {
return (
<div>
<form action="" onSubmit={this.handleSubmit}>
用户名<input onChange={this.saveUsername} type="text" name="username" />
密码<input onChange={this.savePassword} type="password" name="password" />
<button>登陆</button>
</form>
</div>
)
}
saveUsername = (e) => {
this.setState({
username:e.target.value
})
}
savePassword = (e) => {
this.setState({
password:e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault() //阻止默认事件 阻止表单提交
const {username,password} = this.state
console.log(username,password);
}
}
ReactDOM.render(<Login />, document.getElementById('app1'))
但是如果我要注册 手机很多的信息 这样的写法就太臃肿了
第一种方法 事件触发时传进去
class Login extends React.Component {
state = {
username: '',
password: ''
}
render() {
return (
<div>
<form action="" onSubmit={this.handleSubmit}>
用户名<input onChange={(e)=>this.saveFormData('username',e)} type="text" name="username" />
密码<input onChange={(e)=>this.saveFormData('password',e)} type="password" name="password" />
<button>登陆</button>
</form>
</div>
)
}
//保存表单数据到state中
saveFormData = (v,e) => {
console.log(v,e.target.value);
this.setState({
[v]:e.target.value //这里要加[]要不然会当你写的是'v' 字符串 不会读变量数据
})
}
}
ReactDOM.render(<Login />, document.getElementById('app1'))
-------------------------------------------------------------------
//第二种刚开始就执行传进去username 函数柯里化
class Login extends React.Component {
state = {
username: '',
password: ''
}
render() {
return (
<div>
<form action="" onSubmit={this.handleSubmit}>
//页面刚开始就会执行 把username传过去
用户名<input onChange={this.saveFormData('username')} type="text" name="username" />
密码<input onChange={this.saveFormData('password')} type="password" name="password" />
<button>登陆</button>
</form>
</div>
)
}
//触发事件时在执行return里的
saveFormData = (v) => {
//console.log(v);
return (e) => {
console.log(v,e.target.value);
this.setState({
[v]:e.target.value
})
}
}
}
ReactDOM.render(<Login />, document.getElementById('app1'))
组件生命周期
基本使用
基本的
componentDidMount
componentWillUnmount
卸载组件ReactDOM.unmountComponentAtNode(document.getElementById(‘app1’))
class Life extends React.Component {
// 状态
state = {
opacity: 1
}
//初始化时 状态更新时
render() {
const { opacity } = this.state
return (
<div>
<h2 style={{ opacity: opacity }}>学不会React怎么办?</h2>
<button onClick={this.death}>不行!</button>
</div>
)
}
//生命周期 组件挂载完毕时调用 不需要等于箭头函数 因为不是拿来用作回调
//组件 完成 挂载
componentDidMount() {
this.timer = setInterval(() => {
let { opacity } = this.state
opacity += 1
if (opacity <= 0) opacity = 1
this.setState({
opacity
})
}, 200);
}
//组件 即将 卸载
componentWillUnmount() {
clearTimeout(this.timer)
}
death = () => {
//也可以在这里关闭定时器
//卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('app1'))
}
}
//渲染到页面挂载到页面mount 卸载unmount
ReactDOM.render(<Life />, document.getElementById('app1'))
组件的顺序
constructor
componentWillMount 组件将要挂载
render
componentDidMount 组件挂载完毕
使用ReactDOM.unmountComponentAtNode()卸载之前会调这个函数
componentWillUnmount 组件即将卸载
当调用setState()时
相当于组件更新的阀门 默认返回true 返回假则不往下走 返回真则继续往下走
shouldComponentUpdate( )
组件将要更新的钩子
componentWillUpdate
render
组件更新完毕的钩子
componentDidUpdate
当调用forceUpdate()强制更新时
比更新少了个阀门
不想对状态做出修改 就像让你更新一下就用这个
组件将要更新的钩子
componentWillUpdate
render
组件更新完毕的钩子
componentDidUpdate
总结代码
//创建组件
class Life extends React.Component {
//构造器
constructor(props) {
super(props)
console.log('Life-constructor');
// 初始化状态
this.state = {
count: 0
}
}
render() {
console.log('Life-render');
const {
count
} = this.state
return (
<div>
<h2>当前求和为{count}</h2>
<button onClick={this.add}>加一</button>
<button onClick={this.force}>强制更新</button>
</div>
)
}
//组件将要挂载
componentWillMount() {
console.log('Life-componentWillMount');
}
//组件 完成 挂载
componentDidMount() {
console.log('Life-componentDidMount');
}
//------------------------------------------------------------------------------
// 当setState调用 默认返回true 返回假则不往下走
shouldComponentUpdate() {
console.log('Life-shouldComponentUpdate');
return true
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('Life-componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate() {
console.log('Life-componentDidUpdate');
}
//------------------------------------------------------------------------------
// 当要卸载前
componentWillUnmount() {
console.log('Life-componentWillUnmount');
}
add = () => {
let { count } = this.state
this.setState({
count: count + 1
})
}
force = () => {
this.forceUpdate()
}
}
//渲染组件
ReactDOM.render(<Life />, document.getElementById('app1'))
父组件render流程
先贴代码 A组件的name传给B组件展示
class A extends React.Component {
state = {
carName: '奔驰' //A组件只定义不展示
}
render() {
return (
<div>
<div>A</div> {/*未展示A状态里的汽车*/}
<button onClick={this.changeCar}>换车</button>
<B {...this.state} />
</div>
)
}
changeCar = () => {
this.setState({
carName: '奥托'
})
}
}
class B extends React.Component {
componentWillReceiveProps(props){
console.log('子组件将要接受标签属性(参数)componentWillReceiveProps',props);
}
render() {
console.log(this.props);
return (
<div>
<div>B</div>
<div>{this.props.carName}</div> {/*B组件里展示A传过来的汽车*/}
</div>
)
}
}
//渲染组件
ReactDOM.render(<A />, document.getElementById('app1'))
生命周期
一旦父组件的render执行,那么子组件就会执行
组件 将要 接受 标签属性(参数)
componentWillReceiveProps(props)
但是你会发现 父组件的props第一次传过来(也就时页面第一次渲染)是不会调用的
再次该变父组件的props值 则就会调用这个钩子函数
然后就和更新状态一样
shouldComponentUpdate( ) 阀门
组件将要更新的钩子
componentWillUpdate
render
组件更新完毕的钩子
componentDidUpdate(preProps,preState)
新版本的生命周期
新版本即将废弃的钩子
以下三个前面要加UNSAFE_
componentWillMount
componentWillUpdate
componentWillReceiveProps(props)
但是会废弃的 所以最好新版本不要用这三个
新版本的钩子直接看钩子图 新加的使用场景极其罕见
所以最常用的只有三个
DidMount
DidUpdate
WillUnmount
新版钩子的用法
新版本的钩子
static getDerivedStateFromProps(props,state){
//返回一个state对象 或者null
//如果返回state对象 则该组件state等于对象值且值不能修改
//若state的值在任何时候都取决于props,那么可以使用,但是不是必须
//因为构造器里也可以收到
return null
return {props}
return {name:"你好"}
}
//但是此组件会让代码难以维护等等 所以使用场景极其罕见
这个函数在更新前做点事情 比如拿到更新前节点的滚动条数据
getSnapshotBeforeUpdate(){
return 快照值 返回值会传给componentDidUpdate(preProps,preState,v)用第三个参数接
}