React官方文档学习笔记
React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。
单文件引入使用模板:
- script引入:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 加载 React。-->
<!-- 注意: 部署时,将 "development.js" 替换为 "production.min.js"。-->
<!-- 三者的引入顺序会影响 -->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
<!--<script type="text/javascript"> -->
<!-- 快速使用babel转换jsx的混合复杂语法 -->
<script type="text/babel">
let ele = <h1>hello,world</h1>
ReactDOM.render(ele,document.getElementById("root"))
</script>
JSX
看起来很像 XML 的 JavaScript 语法扩展。
Vue和React区别
- vue是html里面嵌入js的一些语法,l通过{{}}等方式简单的使用
- react是将html放在js当中作为变量模板化使用,使得html也作为变量方式拥有了更多的js的特性。
语法
-
在 {} 大括号里面放置任何有效的js代码,语法、函数调用。。。
-
jsx也可以是表达式
function getGreeting(user) { if (user) { return <h1>Hello, {formatName(user)}!</h1>; } return <h1>Hello, Stranger.</h1>; }
-
JSX 特定属性
//你可以通过使用引号,来将属性值指定为字符串字面量: const element = <div tabIndex="0"></div>; //也可以使用大括号{},来在属性值中插入一个 JavaScript 表达式: const element = <img src={user.avatarUrl}></img>;
-
因为 JSX 语法上更接近 JavaScript 而不是 HTML
所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称
class 变成了 className,而 tabindex 则变为 tabIndex。(class是关键字了避免冲突)
-
假如一个标签里面没有内容,你可以使用 /> 来闭合标签,就像 XML 语法一样:
const element = <img src={user.avatarUrl} />;
-
JSX 标签里能够包含很多子元素,只能有一个根元素:
const element = ( <div> <h1>Hello!</h1> <h2>Good to see you here.</h2> </div> );
-
防止xss注入攻击-----会在内部进行以此转义
-
jsx表示对象:
Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。
以下两种示例代码完全等效:const element = ( <h1 className="greeting">Hello, world! </h1> ); //相当于: const element = React.createElement( 'h1', {className: 'greeting'}, 'Hello, world!' );
React.createElement() 会预先执行一些检查,以帮助你编写无错代码,但实际上它创建了一个这样的对象:
// 注意:这是简化过的结构 const element = { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } };
这些对象被称为 “React 元素”。它们描述了你 -->希望在屏幕上看到的内容。React 通过读取这些对象,然后使用它们来构建 DOM 以及保持随时更新。
**注:*在单文件当中使用jsx语法,有时快捷键做注释,在分行的代码中会有html和js注释上的冲突,可以{/ … */}来将他们都作为js代码进行注释
JSX元素渲染
<script type="text/babel">
// React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性。一个元素就像电影的单帧:它代表了某个特定时刻的 UI。
// React 只更新它需要更新的部分
function tick() {
let element = (
<div>
<h1>hello! EveryBody!</h1>
<h1>It is {new Date().toLocaleTimeString()}!</h1>
</div>
)
ReactDOM.render(element, document.getElementById('root'));
}
setInterval(tick,1000)
</script>
组件和props
-
函数组件和Class组件
从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素。
组件名必须大写字母开头
// 1、函数组件,props是形参,随意取, function WelCome (props){ return <h1>{props.name},Welcome to there!</h1>; } //2、class组件,只能写成props,thia.props来自react.Component class Hello extends React.Component{ render(){ return <h1> Hello! {this.props.name}</h1>; } }
-
渲染组件
当 React它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”。
const ele = <WelCome name="zxc"/> ReactDOM.render(ele,document.getElementById("root"))
-
组合组件(自下而上)
function App(){ return( <div> <WelCome name="zxc"/> <Hello name="zxc"/> </div> ) }
注:学会拆分组件(自上而下),及属性prop嵌套的区分
-
Props 的只读性:所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
State和生命周期
State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。prop是外部传入的
注:将函数组件转换成 class 组件,只有Class组件能够接受state
class Clock extends React.Component {
//class 构造函数,在该函数中为 this.state 赋初值:
constructor(props) {
//Class 组件应该始终使用 props 参数(传给父类)来调用父类的构造函数。
super(props);
//状态变量,相当于vue中的data
this.state = { date: new Date() };
}
//生命周期方法
//componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
componentDidMount() {
this.timerID = setInterval(() => this.tick(),1000);
}
componentWillUnmount() {
clearInterval(this.timerID); //清除计时器:
}
tick() {
//this.setState() 来时刻更新组件 state:
this.setState({
date: new Date()
});
}
render() {
return ( //将函数组件内容通过return方式在这里使用
<div>
<h1>Hello, world!</h1>
{/*this.props 和 this.state 是 React 本身设置的,且都拥有特殊的含义*/}
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
ReactDOM.render(
<Clock />,
document.getElementById('root')
);
State使用注意:
1、State不要直接修改,用this.setState({})来修改
构造函数是唯一可以给this.state初始赋值的地方,
2、state可能是异步的
出于性能考虑,React 可能会把多个
setState()
调用合并成一个调用。因为
this.props
和this.state
可能会异步更新,所以你不要依赖他们的值来更新下一个状态。eg:
this.setState({ counter: this.state.counter + this.props.increment, });
要解决这个问题,可以让
setState()
接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:// Correct this.setState((state, props) => ({ counter: state.counter + props.increment }));
3、State的更新合并
class Clock extends React.Component { constructor(props) { super(props); this.state = { posts: [], comments: [] }; } //setState() 来单独地更新它们: //这里的合并是浅合并,所以 this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments。 componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); } }
4、State数据向下传递的,
class Hello extends React.Component{ render(){ return ( //组件可以选择把它的 state 作为 props 向下传递到它的子组件中: <FormattedDate date={this.state.date} /> ) } } //FormattedDate 组件会在其 props 中接收参数 date,但是组件本身无法知道它是来自于 Clock 的 state,或是 Clock 的 props,还是手动输入的: function FormattedDate(props) { return <h2>It is {props.date.toLocaleTimeString()}.</h2>; }
5、每个组件都是真正独立的
function App() { return ( <div> {/* Clock组件都是同一个组件构造出来的,但是他们有彼此式独立的(可以有独立的props等*/} <Clock /> <Clock /> <Clock /> </div> ); } ReactDOM.render( <App />, document.getElementById('root') );
React的常用生命周期图解
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wU0K2OgZ-1641218373735)(C:\Users\zhangxingcai\AppData\Roaming\Typora\typora-user-images\image-20220102155911113.png)]
事件处理
-
**命名:**小驼峰
-
写法:
传统的 HTML:
<button onclick="activateLasers()"> Activate Lasers </button>
在 React 中略微不同:
<button onClick={activateLasers}> Activate Lasers </button>
在 React 中另一个不同点是你不能通过返回 false
的方式阻止默认行为。你必须显式的使用 preventDefault
<a href="#" οnclick="console.log('The link was clicked.'); return false">
Click me
</a>
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
- 使用:
<script type="text/babel">
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
console.log(1,this) //this指向组件实例
// 为了在回调中使用 `this`,重新绑定为这个Toggle组件实例
// this.handleClick = this.handleClick.bind(this);
}
handleC = ()=>{
console.log(11)
}
handleClick() {
console.log(this) //undefined,这里的handleClick是通过
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render() {
return (
//handleClick在代码中是作为onClick的回调,所以不是通过实例调用的,是直接调用的
//类中的方法默认开启了局部的严格模式,因此handleClick中的this为undefined
//方法传参
<div>
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.handleClick.bind(this)}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
</div>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
</script>
条件渲染
-
if或者条件运算符
function UserGreeting(props) { return <h1>Welcome back!</h1>; } function GuestGreeting(props) { return <h1>Please sign up.</h1>; } //根据isLoggedIn条件渲染 function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return <UserGreeting />; } return <GuestGreeting />; } ReactDOM.render( // Try changing to isLoggedIn={true}: <Greeting isLoggedIn={false} />, document.getElementById('root') );
-
元素变量
function LoginButton(props) { return ( <button onClick={props.onClick}> Login </button> ); } function LogoutButton(props) { return ( <button onClick={props.onClick}> Logout </button> ); } //元素变量button class LoginControl extends React.Component { constructor(props) { super(props); this.handleLoginClick = this.handleLoginClick.bind(this); this.handleLogoutClick = this.handleLogoutClick.bind(this); this.state = {isLoggedIn: false}; } handleLoginClick() { this.setState({isLoggedIn: true}); } handleLogoutClick() { this.setState({isLoggedIn: false}); } render() { const isLoggedIn = this.state.isLoggedIn; let button; if (isLoggedIn) { button = <LogoutButton onClick={this.handleLogoutClick} />; } else { button = <LoginButton onClick={this.handleLoginClick} />; } return ( <div> <Greeting isLoggedIn={isLoggedIn} /> {button} </div> ); } } ReactDOM.render( <LoginControl />, document.getElementById('root') );
-
&& 运算符
通过花括号包裹代码,你可以在 JSX 中嵌入任何表达式。这也包括 JavaScript 中的逻辑与 (&&) 运算符。
function Mailbox(props) { const unreadMessages = props.unreadMessages; return ( <div> <h1>Hello!</h1> {unreadMessages.length > 0 && <h2> You have {unreadMessages.length} unread messages. </h2> } </div> ); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( <Mailbox unreadMessages={messages} />, document.getElementById('root') );
-
三目运算符
render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> The user is <b>{isLoggedIn ? 'currently' : 'not'}</b> logged in. </div> ); } //也可以用于较为复杂的表达式中,虽然看起来不是很直观 render() { const isLoggedIn = this.state.isLoggedIn; return ( <div> {isLoggedIn ? <LogoutButton onClick={this.handleLogoutClick} /> : <LoginButton onClick={this.handleLoginClick} /> } </div> ); }
-
阻止组件渲染
function WarningBanner(props) { if (!props.warn) { return null; } return ( <div className="warning"> Warning! </div> ); } class Page extends React.Component { constructor(props) { super(props); this.state = {showWarning: true}; this.handleToggleClick = this.handleToggleClick.bind(this); } handleToggleClick() { this.setState(state => ({ showWarning: !state.showWarning })); } render() { return ( <div> <WarningBanner warn={this.state.showWarning} /> <button onClick={this.handleToggleClick}> {this.state.showWarning ? 'Hide' : 'Show'} </button> </div> ); } } ReactDOM.render( <Page />, document.getElementById('root') );
在组件的
render
方法中返回null
并不会影响组件的生命周期。
列表和Key
我们使用 Javascript 中的 map()
方法来遍历数组,实现列表的渲染
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
//key 用于 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。
//key最好是一个独一无二的字符串,通常使用数组当中每一项的id ,当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key:
//key 应该放在map方法当中,遍历的的数组内的就近组件或者元素之上
// key会传递信息给 React ,但不会传递给你的组件
<li key={number.toString()}>
{number}
</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
表单
在 React 里,HTML 表单元素的工作方式和其他的 DOM 元素有些不同,这是因为表单元素通常会保持一些内部的 state。
在 React 中,可变状态(mutable state)通常保存在组件的 state 属性中,并且只能通过使用 setState()
来更新。
受控组件:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('提交的名字: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
);
}
}
-
textarea组件
class EssayForm extends React.Component { constructor(props) { super(props); this.state = { value: '请撰写一篇关于你喜欢的 DOM 元素的文章.' }; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('提交的文章: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> 文章: <textarea value={this.state.value} onChange={this.handleChange} /> </label> <input type="submit" value="提交" /> </form> ); } }
-
select标签
class FlavorForm extends React.Component { constructor(props) { super(props); this.state = {value: 'coconut'}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event) { this.setState({value: event.target.value}); } handleSubmit(event) { alert('你喜欢的风味是: ' + this.state.value); event.preventDefault(); } render() { return ( <form onSubmit={this.handleSubmit}> <label> 选择你喜欢的风味: <select value={this.state.value} onChange={this.handleChange}> <option value="grapefruit">葡萄柚</option> <option value="lime">酸橙</option> <option value="coconut">椰子</option> <option value="mango">芒果</option> </select> </label> <input type="submit" value="提交" /> </form> ); } }
总的来说,这使得
<input type="text">
,<textarea>
和<select>
之类的标签都非常相似—它们都接受一个value
属性,你可以使用它来实现受控组件。注意
你可以将数组传递到
value
属性中,以支持在select
标签中选择多个选项:<select multiple={true} value={['B', 'C']}>
文件 input 标签
在 HTML 中,
<input type="file">
允许用户从存储设备中选择一个或多个文件,将其上传到服务器,或通过使用 JavaScript 的 File API 进行控制。<input type="file" />
因为它的 value 只读,所以它是 React 中的一个非受控组件。将与其他非受控组件在后续文档中一起讨论。
处理多个输入
当需要处理多个
input
元素时,我们可以给每个元素添加name
属性,并让处理函数根据event.target.name
的值选择要执行的操作。例如:
class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGuests: 2 }; this.handleInputChange = this.handleInputChange.bind(this); } handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; this.setState({ [name]: value }); } render() { return ( <form> <label> 参与: <input name="isGoing" type="checkbox" checked={this.state.isGoing} onChange={this.handleInputChange} /> </label> <br /> <label> 来宾人数: <input name="numberOfGuests" type="number" value={this.state.numberOfGuests} onChange={this.handleInputChange} /> </label> </form> ); } }
这里使用了 ES6 计算属性名称的语法更新给定输入名称对应的 state 值:
例如:
this.setState({ [name]: value});
等同 ES5:
var partialState = {}; partialState[name] = value;this.setState(partialState);
另外,由于
setState()
自动将部分 state 合并到当前 state, 只需调用它更改部分 state 即可。受控输入空值
在受控组件上指定
value
的 prop 会阻止用户更改输入。如果你指定了value
,但输入仍可编辑,则可能是你意外地将value
设置为undefined
或null
。下面的代码演示了这一点。(输入最初被锁定,但在短时间延迟后变为可编辑。)
ReactDOM.render(<input value="hi" />, mountNode); setTimeout(function() { ReactDOM.render(<input value={null} />, mountNode); }, 1000);
受控组件的替代品——非受控组件
状态提升
通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 加载 React。-->
<!-- 注意: 部署时,将 "development.js" 替换为 "production.min.js"。-->
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<!-- 加载我们的 React 组件。-->
<!-- <script src="like_button.js"></script> -->
</head>
<body>
<div id="root"></div>
</body>
<script type="text/babel">
//温度转换
function toCelsius(fahrenheit) {
return fahrenheit === ''?'':(fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return celsius===''?'':(celsius * 9 / 5) + 32;
}
//判断税是否沸腾
function Boil(props) {
if (props.temp > 100) {
return <h1>水沸腾了~</h1>
}
return props.temp!==''?<h1>水儿还在睡觉觉!</h1>:<h1>请在任一输入框输入温度值:</h1>
}
// 填写温度的表单组件, 状态提升
function TempFrom(props) {
function handleTempChange(e) {
const reg = /^[0-9]+\.?[0-9]*$/;
let v = e.target.value
//利用正则判断输入框中是否数字
let value = v.search(reg) !== -1? v :''
props.handleChange(value)
}
return <div>
<fieldset>
<legend>填写{props.type === 'c'?'摄氏温度':'华氏温度'}:</legend>
<input value={props.temp} onChange={handleTempChange}/>
</fieldset>
</div>
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = { //初始化state状态
temperature: '',
type: 'c'
}
}
//动态更新华氏温度
handleC(v) {
this.setState({
temperature: v,
type: 'c'
})
}
//动态更新摄氏温度
handleF(v) {
this.setState({
temperature: v,
type: 'f'
})
}
render() {
//处理温度在线转换
let type = this.state.type
let temp = this.state.temperature
let celsuis = type === 'c'?temp:toCelsius(temp)
let fahrenheit = type === 'f'?temp:toFahrenheit(temp)
return (
<div>
<TempFrom
type='c'
temp={celsuis}
handleChange={this.handleC.bind(this)}
/>
<TempFrom
type='f'
temp={fahrenheit}
handleChange={this.handleF.bind(this)}
/>
<Boil temp={celsuis}/>
</div>
)
}
}
ReactDOM.render(
<App/>,
document.getElementById('root')
)
</script>
</html>
组合vs继承
React 有十分强大的组合模式。我们推荐使用组合而非继承来实现组件间的代码重用
在 Facebook,我们在成百上千个组件中使用 React。我们并没有发现需要使用继承来构建组件层次的情况。
——不建议使用继承
组合
-
包含关系
props.children:类似于vue当中的插槽
使用一个特殊的
children
prop 来将他们的子组件传递到渲染结果中<script type="text/babel"> function Child(props) { return <div> {props.children} </div> } class App extends React.Component { constructor(props) { super(props); this.state = { title:'children Props', msg:'将他们的子组件传递到渲染结果中' } } render() { return ( <Child> <h1>{this.state.title}使用</h1> <p>{this.state.msg}</p> </Child> ) } } ReactDOM.render( <App/>, document.getElementById('root') ) </script>
少数情况下,你可能需要在一个组件中预留出几个“洞”。这种情况下,我们可以不使用
children
,而是自行约定:将所需内容传入 props,并使用相应的 prop。类似于vue当中的具名插槽
function SplitPane(props) { return ( <div className="SplitPane"> <div className="SplitPane-left"> {props.left} </div> <div className="SplitPane-right"> {props.right} </div> </div> ); } function App() { return ( <SplitPane left={ <Contacts /> } right={ <Chat /> } /> ); }
特例关系:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
组合也同样适用于以 class 形式定义的组件:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children} </FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login} onChange={this.handleChange} /> <button onClick={this.handleSignUp}> Sign Me Up! </button> </Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
React哲学