IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 第一章:React 基础知识 -> 正文阅读

[JavaScript知识库]第一章:React 基础知识


初识React


步骤一:添加一个DOM容器到HTML

<div id="app"></div>

步骤二:添加Script标签

// 加载 React
<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>
// 加载 React组件
<script src="app.js"></script>

步骤三:创建一个React组件

const e = React.createElement;
class LikeButton extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			liked: false
		};
	}
	render () {
		if (this.state.liked) {
			return 'You liked this.';s
		}
		// 函数调用方式
		return e(
			'button',
			{onClick: () => this.setState({
				liked: true
			})},
			'Like'
		)
		// JSX方式
		return (
			<button onClick={() => this.setState({ 
			liked: true})}
			>
			Like
			</button>
		)
	}
}
const domContainer = document.querySelector('#app');
ReactDOM.render(e(LikeButton), domContainer);

添加JSX脚本

<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>

创建新的React应用

// 前提是要安装node
// 执行命令
npx create-react-app my-app
cd my-app
npm start

JSX 了解


/*
	在下面的例子中,我们声明了一个名为 name 的变量,然后在 JSX 中使用它,并将它包裹在大括号中:
*/
const name = 'heqiuyu';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
	element,
	document.getElementById('app')
);
/*
	在下面的示例中,我们将调用 JavaScript 函数 formatName(user) 的结果,并将结果嵌入到 <h1> 元素中。
*/
function formatName (user) {
	return user.firstName + ' ' + user.lastName; 
}
const user = {
	firstName: 'heqiuyu',
	lastName: 'baixiaoyun'
};
const element = (
	<h1>Hello, {formatName(user)}</h1>
);
ReactDOM.render(
	element,
	document.getElementById('app')
);

JSX 特定属性

// 你可以通过使用引号,来将属性值指定为字符串字面量:
const ele = <div tabIndex="0"></div>
// 也可以使用大括号,来在属性值中插入一个 JavaScript 表达式:
const ele = <img src={user.avatarUrl} />
/*
	***注意:在属性中嵌入 JavaScript 表达式时,不要在大括号外面加上引号。你应该仅使用引号(对于字符串值)或大括号(对于表达式)中的一个,对于同一属性不能同时使用这两种符号。***
*/

元素渲染


// 第一步:
<div id="app"></div>

// 第二步:
const ele = <h1>Hello, world</h1>
ReactDOM.render(ele, document.getElementById('app'));

更新已渲染的元素

// React 元素是不可变对象。一旦被创建,你就无法更改它的子元素或者属性
function tick () {
	const ele = (
		<div>
			<h1>Hello, world</h1>
			<h2>It is {new Date().toLocaleTimeString()}</h2>
		</div>
	)
	ReactDOM.render(
		ele,
		document.getElementById('app')
	);
}

setInterval(tick, 1000);

组件 & Props


组件,从概念上类似于 JavaScript 函数。它接受任意的入参(即 “props”),并返回用于描述页面展示内容的 React 元素

函数式组件

function Welcome (props) {
	return <h1>Hello, {props.name}</h1>;
}

类组件

class Welcome extends React.Component {
	render () {
		return <h1>Hello, {this.props.name}</h1>;
	}
}

渲染组件

// 例
const ele = <Welcome name="heqiuyu" />;

当 React 元素为用户自定义组件时,它会将 JSX 所接收的属性(attributes)以及子组件(children)转换为单个对象传递给组件,这个对象被称之为 “props”

function Welcome (props) {
	return <h1>Hello, {props.name}</h1>;
}
const ele = <Welcome  name="heqiuyu" />;
ReactDOM.render(
	ele,
	document.getElementById('app')
);
/*
执行流程:
1. 调用 ReactDOM.render() 函数,并传入 <Welcome name="Sara" /> 作为参数。
2. React 调用 Welcome 组件,并将 {name: 'Sara'} 作为 props 传入。
3. Welcome 组件将 <h1>Hello, Sara</h1> 元素作为返回值。
4. React DOM 将 DOM 高效地更新为 <h1>Hello, Sara</h1>。
*/

提取组件

将组件拆分为更小的组件

// Comment组件
function Comment (props) {
	return (
		<div className="Comment">
			<UserInfo user={props.author} />
			<div className="Comment-text">
				{props.text}
			</div>
			<div className="Comment-date">
				{formatDate(props.date)}
			</div>
		</div>
	)
}

该组件用于描述一个社交媒体网站上的评论功能,它接收 author(对象),text (字符串)以及 date(日期)作为 props。

下面就来提取组件

// 第一步:提取Avatar组件
function Avatar (props) {
	return (
		<img className="Avatar" 
			src={props.user.avatarUrl}
			alt={props.user.name}
		/>
	)
}
// 第二步:提取UserInfo组件
function UserInfo (props) {
	return (
		<div className="UserInfo">
			<Avatar user={props.user} />
			<div className="UserInfo-name">
				{props.user.name}
			</div>
		</div>
	)
}

State & 生命周期

在元素渲染章节中,我们只了解了一种更新 UI 界面的方法。通过调用 ReactDOM.render() 来修改我们想要渲染的元素:

// 实现时钟封装
class Clock extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			date: new Date()
		};
	}
	componentDidMount () {
		this.timerID = setInterval(() => this.tick(), 1000);
	}
	componentWillUnmount () {
		clearInterval(this.timerID);
	}
	tick () {
		this.setState({
			date: new Date();
		})
	}
	render () {
		return (
			<div>
				<h1>Hello, world</h1>
				<h2>
					It is {this.state.date.toLocaleTimeString()}
				</h2>
			</div>
		)
	}
}
ReactDOM.render(
	<Clock />,
	document.getElementById('app')
);
/*
	1. 当 <Clock /> 被传给 ReactDOM.render()的时候,React 会调用 Clock 组件的构造函数。因为 Clock 需要显示当前的时间,所以它会用一个包含当前时间的对象来初始化 this.state。我们会在之后更新 state。
	2. 之后 React 会调用组件的 render() 方法。这就是 React 确定该在页面上展示什么的方式。然后 React 更新 DOM 来匹配 Clock 渲染的输出。
	3. 当 Clock 的输出被插入到 DOM 中后,React 就会调用 ComponentDidMount() 生命周期方法。在这个方法中,Clock 组件向浏览器请求设置一个计时器来每秒调用一次组件的 tick() 方法。
	4. 浏览器每秒都会调用一次 tick() 方法。 在这方法之中,Clock 组件会通过调用 setState() 来计划进行一次 UI 更新。得益于 setState() 的调用,React 能够知道 state 已经改变了,然后会重新调用 render() 方法来确定页面上该显示什么。这一次,render() 方法中的 this.state.date 就不一样了,如此以来就会渲染输出更新过的时间。React 也会相应的更新 DOM。
	5. 一旦 Clock 组件从 DOM 中被移除,React 就会调用 componentWillUnmount() 生命周期方法,这样计时器就停止了
*/

正确地使用State

1.不要直接修改State

// 次代码不会重新渲染组件
this.state.comment = 'Hello';
// 而是应该使用 setState():
this.setState({comment: 'Hello'});

2. State的更新可能是异步的

/*
	因为 this.props 和 this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态
*/
// 例如,此代码可能会无法更新计数器:
this.setState({
  counter: this.state.counter + this.props.increment,
});

要解决这个问题,可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数:

this.setState((state, props) => ({
  counter: state.counter + props.increment
}));

3. State的更新会被合并

当你调用 setState() 的时候,React 会把你提供的对象合并到当前的 state

// 例
constructor(props) {
	super(props);
	this.state = {
		posts: [],
		comments: []
	}
}
// 然后你可以分别调用 setState() 来单独地更新它们:
componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }
/*
	这里的合并是浅合并,所以 this.setState({comments}) 完整保留了 this.state.posts, 但是完全替换了 this.state.comments
*/

数据是向下流动的

重点:组件可以选择把它的 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>;
}

事件处理

React事件的命名采用小驼峰式(camelCase),而不是纯小写
使用JSX语法时你需要传入一个函数作为事件处理函数,而不是一个字符串

// 传统写法
<button onclick="handleclick()"></button>
// react写法
<button onClick={handleClick}></button>

项事件处理程序传递参数

// 通过箭头函数方式,事件对象必须显式进行传递
<button onClick={(e) => this.delete(id, e)}></button>
// 通过bind方式,事件对象进行隐式传递
<button onClick={this.delete.bind(this, id)}></button>

条件渲染

在 React 中,你可以创建不同的组件来封装各种你需要的行为。然后,依据应用的不同状态,你可以只渲染对应状态下的部分内容。

与运算符&&

通过花括号包裹代码,你可以在JSX中嵌入任何表达式

function fn (props) {
	const list = props.list;
	return (
		<>
			{
				list.length > 0
				&&
				<h2>渲染内容</h2>
			}
		</>
	)
}

之所以能这样做,是因为在 JavaScript 中,true && expression 总是会返回 expression, 而 false && expression 总是会返回 false

三目运算符

语法

condition ? true : false
// 例
render () {
	const isLogin = this.state.isLogin
	return (
		<>
			the user is <b>{isLogin ? 'currently' : 'not'}</b> logged in.
		</>
	)
}

列表 & Key

基础列表组件

通常你需要在一个组件中渲染列表。

我们可以把前面的例子重构成一个组件,这个组件接收 numbers 数组作为参数并输出一个元素列表

function NumberList (props) {
	const numbers = props.numbers
	const listItems = numbers.map((number) =>
		<li key={number.toString()}>
			{number}
		</li>
	);
	return (
		<ul>{listItems}</ul>
	);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
	<NumberList numbers={numbers} />,
	document.getElementById('app')
);

当我们运行这段代码,将会看到一个警告 a key should be provided for list items,意思是当你创建一个元素时,必须包括一个特殊的 key 属性

表单

受控组件

渲染表单的 React 组件还控制着用户输入过程中表单发生的操作

总的来说,这使得 , 和 之类的标签都非常相似—它们都接受一个 value 属性,你可以使用它来实现受控组件

处理多个输入

当需要处理多个 input 元素时,我们可以给每个元素添加 name 属性,并让处理函数根据 event.target.name 的值选择要执行的操作

// 关键代码 ES6
this.setState({
	[name]: value
})

// ES5
var payLoad = {};
payLoad[name] = value;
this.setState(payLoad);

状态提升

通常,多个组件需要反映相同的变化数据,这时我们建议将共享状态提升到最近的共同父组件中去

无障碍辅助功能

语义化HTML

有时,语义化的 HTML 会被破坏。比如当在 JSX 中使用

元素来实现 React 代码功能的时候,又或是在使用列表(
    • )和 HTML 时。 在这种情况下,我们应该使用 React Fragments 来组合各个组件

import React, { Fragment } from 'react';

function ListItem ({item}) {
	return (
		<Fragment>
			<dt>{item.term}</dt>
			<dd>{item.description}</dd>
		</Fragment>
		{/* 也可以用下面方式 */}
		<>
			<dt>{item.term}</dt>
			<dd>{item.description}</dd>
		</>
	)
}

function Glossary (props) {
	return (
		<dl>
			{
				props.items.map(item => (
					<ListItem  item={item} key={item.id} />
				))
			}
		</dl>
	)
}

Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法

API

React.createContext

const MyContext = React.createContext(defaultValue)

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

Context.Provider

<MyContext.Provider value={ /* 某个值*/ } >

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。

Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。

当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新

Class.contextType

class MyClass extends React.Component {
	componentDidMount () {
		let value = this.context
	}
	render () {
		let value = this.context
	}
}
MyClass.contextType = MyContext

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中在这里插入代码片

也可以使用public class fields语法

class MyClass extends React.Component {
	static contextType = MyContext
	render () {
		let value = this.context
	}
}

Context.Consumer

<MyContext.Consumer>
	{value => /* 基于 context 值进行渲染 */ }
</MyContext.Consumer>

高阶组件

含义:高阶组件是参数为组件,返回值为新组件的函数

const EnhancedComponent = higherOrderComponent(WrappedComponent);

组件是将props转换为UI,而高阶组件是将组件转换为另一个组件

Refs and the DOM

Refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素

在典型的 React 数据流中,props 是父组件与子组件交互的唯一方式。要修改一个子组件,你需要使用新的 props 来重新渲染它。但是,在某些情况下,你需要在典型数据流之外强制修改子组件。被修改的子组件可能是一个 React 组件的实例,也可能是一个 DOM 元素。对于这两种情况,React 都提供了解决办法。

创建Refs

class MyComponent extends React.Component {
	constructor(props) {
		super(props);
		// 创建myRef
		this.myRef = React.createRef();
	}
	render () {
		// 通过ref属性使用
		return <div ref={this.myRef} />
	}
}

访问Refs

/*
	当 ref 被传递给 render 中的元素时,对该节点的引用可以在 ref 的 current 属性中被访问
*/
const node = this.myRef.current;

注意:ref 的值根据节点的类型而有所不同:
当 ref 属性用于 HTML 元素时,构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。
当 ref 属性用于自定义 class 组件时,ref 对象接收组件的挂载实例作为其 current 属性。
你不能在函数组件上使用 ref 属性,因为他们没有实例。

为DOM元素添加ref

class CustomTextInput extends React.Component {
	constructor(props) {
		// 1. 创建一个ref来存储textInput的DOM元素
		this.textInput = React.createRef();
	}
	focusTextInput () {
		// 3. 通过current来访问DOM节点
		this.textInput.current.focus();
	}
	render () {
		// 2. 把input的ref关联到构造器里创建的textInput上
		return (
			<input 
				type="text"
				ref={this.textInput}
			/>
			<input
				type="button"
				value="Focus the text input"
				onClick={this.focusTextInput}
			/>
		)
	}
}

React 会在组件挂载时给 current 属性传入 DOM 元素,并在组件卸载时传入 null 值。ref 会在 componentDidMount 或 componentDidUpdate 生命周期钩子触发前更新。

为class组件添加Ref

/*
	如果我们想包装上面的 CustomTextInput,来模拟它挂载之后立即被点击的操作,我们可以使用 ref 来获取这个自定义的 input 组件并手动调用它的 focusTextInput 方法
*/
class AutoFocusTextInput extends React.Component {
	constructor(props) {
		super(props);
		this.textInput = React.createRef();
	}
	componentDidMount () {
		this.textInput.current.focusTextInput();
	}
	render () {
		return (
			<CustomTextInput ref={this.textInput} />
		)
	}
}

使用PropTypes进行类型检查

配置propTypes属性

import PropTypes from 'prop-types';

class Greeting extends React.Component {
	render () {
		return (
			<h1>Hello, {this.props.name}</h1>
		)
	}
}
// 指定props默认值
Greeting.defaultProps = {
	name: 'Stranger'
};
// 校验props数据类型
Greeting.propTypes = {
	name: PropTypes.string
};

应该在React组件的哪个生命周期函数中发起AJAX请求

我们推荐你在 componentDidMount 这个生命周期函数中发起 AJAX 请求。这样做你可以拿到 AJAX 请求返回的数据并通过 setState 来更新组件

使用 AJAX 请求结果去改变组件内部 state

// 1. 数据结构
{
	"items": [
		{"id": 1, "name": "Apples", "price": "$s"},
		{"id": 2, "name": "Peaches", "price": "$5"}
	]
}
class MyComponent extends React.Component {
	constructor(props) {
		super(props);
		this.state = {
			error: null,
			isLoaded: false,
			items: []
		};
	}
	// 2. 请求数据
	componentDidMount () {
		fetch("https://api/example.com/itmes")
		.then(res => res.json())
		.then(
		 (result) => {
			this.setState({
				isLoaded: true,
				items: result.items
			});
		},
		 (error) => {
			this.setState({
				isLoaded: true,
				error
			})
		}
	  )
	}
	// 3. 渲染视图
	render () {
		const { error, isLoaded, items } = this.state;
		if (error) {
			return <div>Error: {error.message}</div>
		} else if (!isLoaded) {
			return <div>Loading...</div>
		} else {
			return (
				<ul>
					{items.map(item =>(
						<li key={item.name}>
							{item.name} {item.price}
						</li>
					))}
				</ul>
			)
		}
	}
}
// 使用Hooks
function MyComponent () {
	const [error, setError] = useState(null);
	const [isLoaded, setIsLoaded] = useState(false);
	const [items, setItems] = useState([]);
	
	useEffect(() => {
		fetch("https://api.example.com/items")
			.then(res => res.json())
			.then(
				(result) => {
					setIsLoaded(true);
					setItems(result.items);
				},
				(error) => {
					setIsLoaded(true);
					setError(error);
				}
			)
	}, [])

	if (error) {
	 	return <div>Error: {error.message}</div>
	 } else if (!isLoaded) {
	 	return <div>Loading...</div>
	 } else {
	 	return (
	 		<ul>
	 			{items.map(item => {
	 				<li key={item.name}>
	 					{item.name} {item.price}
	 				</li>
	 			})}
	 		</ul>
	 	)
	 }
}

组件状态

setState实际做了什么?

setState() 会对一个组件的 state 对象安排一次更新。当 state 改变了,该组件就会重新渲染。

state和props之间的区别是什么?

props和state都是普通的JavaScript对象,他们都是用来保存信息的,不同的是:props是传递给组件的(类似于函数的形参),而state是在组件内被组件自己管理的(类似于在一个函数内声明的变量)(重点区别)

如何更新那些依赖于当前的state的state呢?

给 setState 传递一个函数,而不是一个对象,就可以确保每次的调用都是使用最新版的 state

setState什么时候是异步的?

目前,在事件处理函数内部的 setState 是异步的。

样式与CSS

如何为组件添加CSS的class?

// 传递一个字符串作为className属性
render () {
	return <span className="menu">Menu</span>
}
// CSS 的 class 依赖组件的 props 或 state 的情况很常见
render () {
	let className = 'menu';
	if (this.props.isActive) {
		className += ' menu-active';
	}
	return <span className={className}>menu</span>
}
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-25 12:06:48  更:2021-08-25 12:09:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 12:50:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码