1.入门
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello</title>
</head>
<body>
<div id="test"></div>
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
<script src="../react/babel.min.js"></script>
<script type="text/babel">
// 1.入门
{
// (1).创建虚拟DOM
// const VDOM = <h1>Hello</h1>
// (2).渲染,如果有多个渲染同一个容器,后面的会将前面的覆盖掉
// ReactDOM.render(VDOM,document.getElementById("test"));
}
// 2.JSX语法
{
// const myId = "title";
// const myData = "Hello";
// (1).创建虚拟DOM
// const VDOM = (
// <h1 id={myId.toLocaleUpperCase()}>
// <span className="active" style={{ fontSize: '50px' }}>{myData}</span>
// </h1>
// )
// (2).渲染,如果有多个渲染同一个容器,后面的会将前面的覆盖掉
// ReactDOM.render(VDOM, document.getElementById("test"));
}
// 3.两种创建虚拟DOM的方式
{
// (1).JSX创建虚拟DOM
// const VDOM = (
// <h1 id="title">
// <span className="active" style={{ fontSize: '50px' }}>Hello</span>
// </h1>
// )
// (2).JS创建虚拟DOM
// const vDOM = React.createElement('h1',{id:"title"}, React.createElement('span', {}, 'Hello'));
// 第一种方式会被babel转为第二种方式(devtools元素面板可验证)
// 所以第一种方式是第二种的语法糖,由react提供
}
// 4.两种组件
{
// (1).函数式组件:适于简单组件
// function Welcome(props) {
// return <h1>Hello, {props.name}</h1>;
// }
// ReactDOM.render(<Welcome name="A"/>, document.getElementById("test"));
// (2). 类式组件:适于复杂组件
// class Welcome extends React.Component {
// render() {
// return <h1>Hello, {this.props.name}</h1>
// }
// }
// ReactDOM.render(<Welcome name="B"/>, document.getElementById("test"));
}
// 5.组件嵌套
{
// function GetLi(props) {
// return <li>{props.value}</li>
// }
// class GetUl extends React.Component {
// render() {
// return (
// <ul>
// {
// this.props.arr.map((item, index) =>
// <GetLi value={item} key={index} />
// )
// }
// </ul>
// )
// }
// }
// ReactDOM.render(<GetUl arr={[1, 2, 3, 4]} />, document.getElementById("test"));
}
</script>
</body>
</html>
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
2. 三大属性
(1).state
{
class MyComp extends React.Component {
constructor(props) {
super(props);
this.state = { status: false };
//这里的this必然是实例对象
this.changeStatus = this.changeStatus.bind(this);
//上面这句话,右边的changeStatus是原型对象上的,左边的changeStatus是实例对象上的
}
//类中定义的方法都在原型对象上
render() {
console.log(this);//通过Mycomp的实例调用render方法时,这里的this是该实例对象
return <h1 onClick={this.changeStatus}>状态:{this.state.status ? '正常' : '异常'}</h1>
}
//类中定义的方法都在原型对象上
changeStatus() {
console.log(this);//通过Mycomp的实例调用render方法时,这里的this是该实例对象
//但是当h1标签被点击,从而调用changeStatus方法时,实际调的是它的一个克隆体(全局作用域内)
//此时this理应为window对象,但是会开启局部严格模式,则this最终为undefined
//拿不到正确的this就无法修改status,那咋办?
//有些方法(如call、apply、bind)是可以修改this的
//比如 this.changeStatus.bind(this)会返回一个新的方法并为新方法制定了this
//那么我们希望onClick的回调,是这个新方法
//目前onClick的回调,是原型对象的老方法
//而之所以是原型对象上的方法,是因为实例对象上没有找到名为changeStatus的方法,如果找到了,那么优先用这个
//那么思路就清晰了:给实例对象加个名为changeStatus的属性,属性值是this.changeStatus.bind(this)的返回值
//加属性要在构造器里加
// this.state.status = true;
// console.log(this.state.status);//true
//直接更改,没有“响应式”的效果
//在vue中也是这样,state放在data里,如果只改state的属性,则不触发视图的更改(错×,vue深度监听了对象)
// this.state = { status: !this.state.status };
// console.log(this.state);
//在react中,上面这么写也不行,得借助框架提供的API
this.setState({ status: !this.state.status });
// 如果只是修改state的部分属性,则不会影响其他的属性,这个只是合并并不是覆盖。上面我自己的写法是覆盖
}
}
ReactDOM.render(<MyComp />, document.getElementById("test"));
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
state 简化写法:
class MyComp extends React.Component {
state = { isHot: false };
render() {
return <h1 onClick={this.changeState}>今天天气{this.state.isHot ? '炎热' : '凉爽'}</h1>
}
//箭头函数没有自己的this,会向外找
changeState = () => {
this.setState({ isHot: !this.state.isHot });//异步操作
}
}
ReactDOM.render(<MyComp />, document.getElementById("test"));
/**
* React控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件,setTimeout/setInterval等。
* 大部分开发中用到的都是React封装的事件,比如onChange、onClick等,setState都是异步处理的。
**/
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
React调用setState方法时,会有同步与异步之分。
如果是同步更新,每一个setState都会执行,每次执行会调用一次render(多次渲染)
如果是异步更新,多个setState会仅执行最后一个,这样仅调用一次render(一次渲染)
class MyComp extends React.Component {
state = { isHot: false, cnt: 0 };
render() {
return <h1 onClick={this.changeState}>今天天气{this.state.isHot ? '炎热' : '凉爽'},数量:{this.state.cnt}</h1>
}
changeState = () => {
this.setState({ cnt: this.state.cnt + 1 });
this.setState({ cnt: this.state.cnt + 3 });
this.setState({ cnt: this.state.cnt + 5 });
}
}
ReactDOM.render(<MyComp />, document.getElementById("test"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
这里 cnt 最终为5,也就是仅执行了最后一个setState。
自定义方法的传参
class MyComp extends React.Component {
state = { isHot: false, cnt: 0, num: 5 };
render() {
// return <h1 onClick={(e) => this.changeState(this.state.num, e)}>今天天气{this.state.isHot ? '炎热' : '凉爽'},数量:{this.state.cnt}</h1>
return <h1 onClick={this.changeState.bind(this, this.state.num)}>今天天气{this.state.isHot ? '炎热' : '凉爽'},数量:{this.state.cnt}</h1>
}
changeState = (n, e) => {
this.setState({ cnt: this.state.cnt + n });
console.log(e.target);
}
}
ReactDOM.render(<MyComp />, document.getElementById("test"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
有①箭头函数②bind两种方式
第二种方式e也能读取到
(2).props
props是只读的(read-only)
给组件传值
class Person extends React.Component {
render() {
return (
<ul>
<li>name: {this.props.name}</li>
<li>age: {this.props.age}</li>
<li>gender: {this.props.gender}</li>
</ul>
);
}
}
ReactDOM.render(<Person name="Joy" age="25" gender="male"/>, document.getElementById("test"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
给组件传一个对象
class Person extends React.Component {
render() {
return (
<ul>
<li>name: {this.props.name}</li>
<li>age: {this.props.age}</li>
<li>gender: {this.props.gender}</li>
</ul>
);
}
}
ReactDOM.render(<Person name="Joy" age="25" gender="male"/>, document.getElementById("test"));
const p = { name: "Mike", age: 28, gender: "male" };
ReactDOM.render(<Person {...p}/>, document.getElementById("test2"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
原本在JS中,剩余运算符不能用于展开一个对象,这里能这么写,是因为react+babel允许在自定义组件中写这种语法
纯JS中这么写的话,只能是复制一个对象:
const p1 = {name:"张三",age:"18",sex:"女"};
const p2 = {...p1};
p1.name = "sss";
console.log(p2); //{name:"张三",age:"18",sex:"女"}
//复制时修改属性
const p1 = {name:"张三",age:"18",sex:"女"};
const p2 = {...p1,name : "111",hua:"ss"};
p1.name = "sss";
console.log(p2); //{name: "111", age: "18", sex: "女",hua:"ss"}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
限制:
- 类型检查:propTypes
- 是否必填:propTypes
- 设默认值:defaultProps
//react
class Person extends React.Component {
render() {
return (
<ul>
<li>name: {this.props.name}</li>
<li>age: {this.props.age}</li>
<li>gender: {this.props.gender()}</li>
</ul>
);
}
static propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.string,
gender: PropTypes.func
}
static defaultProps = {
name: "Anonymity",
age: "18",
}
}
const getGender = () => { return "male"; }
ReactDOM.render(<Person gender={getGender}/>, document.getElementById("test"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
以上是在类式组件中使用props,下面是在函数式组件中使用props:
(3).refs
①.字符串形式
官方不推荐,说后续版本可能移除这种方式
class Demo extends React.Component {
render() {
return (
<div>
<input ref="first" type="text" placeholder="Click button to get the input value"/>
<button onClick={this.getFirstValue}>Click</button>
<input onBlur={this.getSecondValue} ref="second" type="text" placeholder="Blur to get the input value"/>
</div>
)
}
getFirstValue = () => {
console.log(this.refs.first.value);
}
getSecondValue = () => {
console.log(this.refs.second.value);
}
}
ReactDOM.render(<Demo/>, document.getElementById("test"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
②.回调形式
将当前DOM元素赋给类自定义属性
class Demo extends React.Component {
render() {
return (
<div>
<input ref={ element => this.first = element } type="text" placeholder="Click button to get the input value" />
<button onClick={this.getFirstValue}>Click</button>
<input ref={element => this.second = element} onBlur={this.getSecondValue} type="text" placeholder="Blur to get the input value"/>
</div>
)
}
getFirstValue = () => {
console.log(this.first.value);
}
getSecondValue = () => {
console.log(this.second.value);
}
}
ReactDOM.render(<Demo />, document.getElementById("test2"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
回调形式有个特点:直接在DOM上写回调时,在重新渲染时,首次的element为null,第二次才是DOM元素。
可以改成从外面传回调。不过即使不这么做也没啥大问题。
class Demo extends React.Component {
state = { isHot: false }
getFirstRef = element => {
this.first = element;
console.log(element);
}
render() {
return (
<div>
<p>Today is {this.state.isHot ? 'hot' : 'cool'}.</p>
{/*<input ref={ element => {this.first = element; console.log(element)}} type="text" placeholder="Click button to get the input value" />*/}
<input ref={ this.getFirstRef } type="text" placeholder="Click button to get the input value" />
<button onClick={this.getFirstValue}>Click</button>
<input ref={ element => this.second = element } onBlur={this.getSecondValue} type="text" placeholder="Blur to get the input value" />
</div>
)
}
getFirstValue = () => {
console.log(this.first.value);
this.setState({ isHot: !this.state.isHot })
}
getSecondValue = () => {
console.log(this.second.value);
}
}
ReactDOM.render(<Demo />, document.getElementById("test1"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
③.API形式
React.createRef()创建React的容器,这个容器是专人专用的,所以每一个ref都需要创建这个。该API会将DOM元素赋值给实例对象的名称为容器名称的属性的current【这个current是固定的】
class Demo extends React.Component {
first = React.createRef();
second = React.createRef();
render() {
return (
<div>
<input ref={ this.first } type="text" placeholder="Click button to get the input value" />
<button onClick={this.getFirstValue}>Click</button>
<input ref={ this.second } onBlur={this.getSecondValue} type="text" placeholder="Blur to get the input value" />
</div>
)
}
getFirstValue = () => {
console.log(this.first.current.value);
}
getSecondValue = () => {
console.log(this.second.current.value);
}
}
ReactDOM.render(<Demo />, document.getElementById("test2"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
事件替代Ref
官方提示我们不要过度的使用ref,如果发生事件的元素刚好是需要操作的元素,就可以使用事件去替代。
class Demo extends React.Component {
first = React.createRef();
render() {
return (
<div>
<input ref={ this.first } type="text" placeholder="Click button to get the input value" />
<button onClick={this.getFirstValue}>Click</button>
<input onBlur={event => console.log(event.target.value)} type="text" placeholder="Blur to get the input value" />
</div>
)
}
getFirstValue = () => {
console.log(this.first.current.value);
}
}
ReactDOM.render(<Demo />, document.getElementById("test3"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
如果发生事件的元素不是需要操作的元素,也有办法
class Demo extends React.Component {
render() {
return (
<div>
<input onChange={ event => this.setState({ first: event.target.value }) } type="text" placeholder="Click button to get the input value" />
<button onClick={ () => console.log(this.state.first) }>Click</button>
<input onBlur={event => console.log(event.target.value)} type="text" placeholder="Blur to get the input value" />
</div>
)
}
}
ReactDOM.render(<Demo />, document.getElementById("test4"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
3.受控组件与非受控组件
React中,表单元素(如<input>、 <textarea> 和 <select>)分为受控组件与非受控组件,我的理解:
- 受控组件:类似vue的双向绑定(推荐)(value不仅用户可以设置,还可由开发人员通过脚本设置)
- 非受控组件:只有 View => Model 的单项流动(value仅由用户设置)
// 非受控
class Login extends React.Component {
username = React.createRef();
password = React.createRef();
render() {
return (
<form action="" onSubmit={this.login}>
<label htmlFor="username">用户名</label>
<input type="text" name="username" id="username" ref={ this.username }/>
<br/>
<label htmlFor="password">密码</label>
<input type="password" name="password" id="password" ref={ this.password }/>
<button>登录</button>
</form>
)
}
login = (e) => {
e.preventDefault();
console.log('username: ', this.username.current.value, 'password: ', this.password.current.value);
}
}
ReactDOM.render(<Login/>, document.getElementById("test"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
// 受控:即时现场验证
class Login extends React.Component {
state = { username: '', password: '' }
render() {
return (
<form action="" onSubmit={this.login}>
<label htmlFor="username">用户名</label>
<input type="text" name="username" id="username"
value={this.state.username} onChange={this.checkUsername} />
<br />
<label htmlFor="password">密码</label>
<input type="password" name="password" id="password"
value={this.state.password} onChange={this.checkPassword} />
<button>登录</button>
</form>
)
}
checkUsername = (e) => {
const usernameRegex = /^[a-zA-Z0-9]{0,8}$/;
const flag = usernameRegex.test(e.target.value);
flag && (this.setState({ username: e.target.value }))
setTimeout(() => {
console.log(flag, this.state.username);
})
}
checkPassword = (e) => {
//长度在 8 到 20 个字符之间,必须包含至少一个大写字母、一个小写字母和一个数字
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{0,20}$/;
const flag = passwordRegex.test(e.target.value);
this.setState({ password: e.target.value });
setTimeout(() => {
console.log(flag, this.state.password);
})
}
login = (e) => {
e.preventDefault();
console.log('username: ', this.username.current.value, 'password: ', this.password.current.value);
}
}
ReactDOM.render(<Login />, document.getElementById("test2"));
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.