文章目录
React基础
React项目
- 创建项目
npx create-react-app my-app
- 运行项目
npm start
元素和组件
React应用程序可以分为两部分:元素 + 组件
function formatName(user) {
return user.firstName + " " + user.lastName;
}
const user = {
firstName: "Harper",
lastName: "Perez",
};
export default function Jsx() {
return <h1>Hello, {formatName(user)}!</h1>;
}
以最简单的React的例子展开来介绍React。
在上面的代码中有两个function,第一个formatName
函数,是一个方法,拼接两个字符串,返回user的全名;第二个Jsx
函数,是一个React组件,返回的是一个Html元素,而Html元素中的又使用{}
包裹了一个变量,这个变量是formatName
函数的返回值,这种将Html元素和JavaScript语法结合起来使用的方式,称为JSX语法,它也是React使用的主要语法。
JSX语法
const elementOne = <img src={user.avatarUrl} alt="" />;
const elementTwo = (
<div>
<h1>Hello!</h1>
<h2>Good to see you here.</h2>
</div>
);
JSX语法的优点:
- JSX防止注入攻击,你可以安全地在JSX中插入用户输入内容。
ReactDOM在渲染所有输入内容之前,默认会进行转译,所有内容在渲染之前都被转换成了字符串,可以有效的防止XSS攻击。 - Babel会将JSX转译为一个名为React.createElement()的函数,这个函数会预先执行一些检查,确保你编写的代码无错。
React元素的渲染
元素是构成React应用的最小砖块。
ReactDOM负责将React元素渲染为DOM。
React元素一旦被创建,就无法更改它的子元素或属性。
React只更新它需要的部分,ReactDOM会将元素及其子元素与它们之前的状态进行比较,然后更新被改变了的部分。
React组件
组件允许你将 UI 拆分为独立可复用的代码片段,并对每个片段进行独立构思。
组件类似与JS中的函数,它可以接受任意的参数(即props),并返回React元素。组件由元素 + 逻辑构成。
在React中组件分为两种:函数式组件和class组件。
函数式组件
function Welcome(props){
return <h1>Hello, {props.name}</h1>;
}
class组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
React组件的渲染
const element = <Welcome name="Sara" />;
当React元素为自定义组件时,它会将JSX所接收的属性以及子组件转换为props传递给组件。
function WarningBanner(props) {
if (!props.warn) {
return null;
}
return <div className="warning">Warning!</div>;
}
当React组件的返回值为null,默认不渲染组件。
组件的组合与提取
通过在父组件中引入import
子组件的方式来实现组件的组合。
当组件的嵌套层级过多时,为了便于组件的维护,可以将其中可复用的组件提取出来,在引入使用。
Props
props:用于获取父组件中的数据。
在函数式组件中的使用:props.data
function UserInfo(props) {
return (
<div className="UserInfo">
<Avatar user={ props.author }/>
<div className="UserInfo-name">
{props.author.name}
</div>
</div>
);
}
在函数式组件中使用props,需要先向函数传入props参数,而在class组件中,则通过this.访问,即class组件上默认带有props属性,无需再通过参数传递。
在class组件中的使用:this.props.data
export default function Page1() {
const { messages1 } = this.props;
return <Mailbox unreadMessages={messages1} />;
}
通过props获取的数据具有只读性,因此子组件无法直接修改props的数据。
如果一定需要在子组件中修改获取的props数据,该怎么做呢?
可以在父组件中获取子组件中需要修改的props,然后在父组件中进行修改(需要使用到【事件处理】,在后面提到)
State
state:用于监控数据的变化。
constructor(props){
super(props);
this.state = {state1: false, };
}
state的内容是组件私有的,在组件内通过this.state.data来访问里面的数据。
当state中的数据需要被多个组件同时使用时,则应该将state放在最近的父组件中,并通过props向下传递获取数据。
生命周期
挂载
- constructor(props):初始化state 和props数据;
- getDerivedStateFromProps(nextProps, prevState):组件初始化和更新时调用;
- render():渲染组件;
- componentDidMount():在组件被挂载到页面后执行,只在挂载时执行一次。
更新
- getDerivedStateFromProps(nextProps, prevState):组件初始化和被更新时调用;
- shouldComponentUpdate(nextProps, nextState):在组件被更新之前执行 (return true 更新 , return false 不更新);
- render(): 渲染页面;
- getSnapshotBeforeUpdate(prevProps, prevState);
- componentDidUpdate():state或props更新后调用。
卸载
- componentWillUnmount():在组件即将被页面剔除时执行。
事件处理
通过元素的onClick、onsubmit等属性来为元素绑定事件,事件处理程序的参数event(事件对象),在事件发生时自动获取。
函数式组件中的事件处理
在函数式组件中,事件处理程序是一个function函数:
function Form() {
function handleSubmit(e) {
e.preventDefault();
}
return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}
事件处理程序:handleSubmit
函数,其中传入的参数e,是事件对象。
class组件中的事件处理
在class组件中,事件处理程序是一个方法:
class Page2 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>
);
}
}
事件处理函数:handleToggleClick
方法
,当需要在函数中使用this.xxx时,则需要在constructor()中为事件绑定this。
事件绑定:this.handleClick = this.handleClick.bind(this);
条件语法
- if 条件语句
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
- 与运算符 &&
{ count && <h1>Messages: {count}</h1>}
- 三目运算符
condition ? true : false
-
阻止组件渲染
当组件返回null时,则组件不会被渲染。
循环 & key
使用循环来渲染元素组件时,需要给每个元素设置key值,且需要保证兄弟元素的key值各不相同。
设置key的作用:帮助React识别哪些元素被改变(添加/删除)了。
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
key值应该被设置在循环渲染的元素上。
return (
<ul>
{numbers.map((number) =>
<ListItem key={number.toString()}
value={number} />
)}
</ul>
);
表单
受控组件:组件内数据的改变不能在通过受控组件来修改,需要在外层组件中修改state,通过使用this.setState方法来修改
this.setState({value: event.target.value});
在React中,input、select、textarea元素的值都可以通过value属性来获取和设置,当input是checkbox类型时,通过checked属性来获取true/false。
handleFruitChange(event) {
this.setState({
fruit: event.target.value,
});
}
<label>
名字:
<input
name="name"
type="text"
value={this.state.name}
onChange={this.handleChange}
></input>
</label>
当表单中有多个输入元素,可以通过给元素设置不同的name,通过name来修改不同输入元素的value。
handleChange(event) {
const target = event.target;
const name = target.name;
const value = target.value;
this.setState({
[name]: value,
});
}
状态提升
当多个组件需要使用相同的可变数据时,可以将这些数据放到他们的最近共同父组件中,通过props来获取父组件中的数据。
组合
在React中,对于组件间的代码重用,不使用继承,使用组合模式
包含关系
无法提前知晓子组件的具体内容,但是需要渲染子组件时,使用一个特殊的props.children
来将自组件传递到渲染结果中
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
特例关系
<div className={'FancyBorder FancyBorder-' + props.color}>
<h1 className="Dialog-title">{props.title}</h1>
<p className="Dialog-message">{props.message}</p>
</div>
<FancyBorder title="Wecome" message="Thank you for visiting our spacecraft!" />
组件可以接受任意props,包括基本数据类型、react元素以及函数。
React应用
如何根据页面设计稿来编写react组件
思路:
-
将 UI 分离为组件(父组件、子组件),并给每个组件命名
例如: 1.FilterableProductTable 2.SearchBar 3.ProductTable 4.ProductCategoryRow 5.ProductRow
-
先渲染UI完成一个静态版本的页面,再实现交互功能(静态版本中不应该使用到state,可以使用props来实现父组件向子组件传递数据)
自上(父组件)而下(子组件)构建应用
-
确定 state 的最小(且完整)表示
- 包含所有产品的原始列表----不可变
- 用户搜索的关键词-----------1.state
- 复选框是否选中的值---------1.state
- 经过搜索筛选的产品列表-----通过计算得出
-
添加反向数据流
低层组件是受控组件,在高层组件中修改数据,操作在低层组件上,但是state在高层组件中,通过在高层组件获取低层的event.target来修改(传递event的方法)。
// 最外层的父组件
export default class FilterableProductTable extends React.Component {
constructor(props) {
super(props);
this.state = {
filterText: "",
inSrockOnly: false,
};
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInSrockOnlyChange = this.handleInSrockOnlyChange.bind(this);
}
// 在父组件中修改数据,但是数据是在子组件中获取的,即反向数据流
handleFilterTextChange(event) {
this.setState({
filterText: event.target.value,
});
}
handleInSrockOnlyChange(event) {
this.setState({
inSrockOnly: event.target.checked,
});
}
render() {
const PRODUCT = [
{
category: "Sporting Goods",
price: "$49.99",
stocked: true,
name: "Football",
},
{
category: "Sporting Goods",
price: "$9.99",
stocked: true,
name: "Baseball",
},
{
category: "Sporting Goods",
price: "$29.99",
stocked: false,
name: "Basketball",
},
{
category: "Electronics",
price: "$99.99",
stocked: true,
name: "iPod Touch",
},
{
category: "Electronics",
price: "$399.99",
stocked: false,
name: "iPhone 5",
},
{
category: "Electronics",
price: "$199.99",
stocked: true,
name: "Nexus 7",
},
];
return (
<div>
<SearchBar
filterText={this.state.filterText}
inSrockOnly={this.state.inSrockOnly}
onFilterTextChange={this.handleFilterTextChange}
onInSrockOnlyChange={this.handleInSrockOnlyChange}
/>
<ProductTable
filterText={this.state.filterText}
inSrockOnly={this.state.inSrockOnly}
products={PRODUCT}
/>
</div>
);
}
}
class SearchBar extends React.Component {
constructor(props) {
super(props);
this.handleFilterTextChange = this.handleFilterTextChange.bind(this);
this.handleInSrockOnlyChange = this.handleInSrockOnlyChange.bind(this);
}
handleFilterTextChange(event) {
this.props.onFilterTextChange(event);
}
handleInSrockOnlyChange(event) {
this.props.onInSrockOnlyChange(event);
}
render() {
return (
<form>
<input
value={this.props.filterText}
type="search"
placeholder="Search..."
onChange={this.handleFilterTextChange}
/>
<br />
<label>
<input
checked={this.props.inSrockOnly}
type="checkbox"
placeholder="Search..."
onChange={this.handleInSrockOnlyChange}
/>{" "}
Only show products in stock
</label>
</form>
);
}
}
function ProductTable(props) {
const rows = [];
let lastCategory = "";
props.products.forEach((product) => {
if (product.name.indexOf(props.filterText) === -1) {
return;
}
if (props.inSrockOnly && product.stocked === false) {
return;
}
if (product.category !== lastCategory) {
rows.push(
<ProductCategoryRow
category={product.category}
key={product.category}
/>
);
lastCategory = product.category;
}
rows.push(<ProductRow product={product} key={product.name} />);
});
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>{rows}</tbody>
</table>
);
}
function ProductCategoryRow(props) {
return (
<tr>
<td colSpan="2" style={{ fontWeight: "600" }}>
{props.category}
</td>
</tr>
);
}
function ProductRow(props) {
const name = props.product.stocked ? (
props.product.name
) : (
<span style={{ color: "red" }}>{props.product.name}</span>
);
return (
<tr>
<td>{name}</td>
<td>{props.product.price}</td>
</tr>
);
}