learn from 《React全家桶:前端开发与实例详解》 https://zh-hans.reactjs.org/tutorial/tutorial.html https://zh-hans.reactjs.org/docs/create-a-new-react-app.html#create-react-app
- 安装 Node.js
- 安装
npm install -g live-server
,配置环境变量 pathC:UsersuserAppDataRoamingnpm
npx create-react-app react_learning
cd react_learning
npm start
1. JSX
对 javascript 的扩展,代码显示更优雅,与 react 配合很好
Babel
目前(2022-07), 并不是所有的 浏览器 都支持 ES6,Babel 可以转译 ES6 -> ES5
head 里包含了 <script src="vendor/babel-standalone.js"></script>
index.html
代码语言:javascript复制<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Project One</title>
<link rel="stylesheet" href="./semantic-dist/semantic.css" />
<link rel="stylesheet" href="./style.css" />
<script src="vendor/babel-standalone.js"></script>
<script src="vendor/react.js"></script>
<script src="vendor/react-dom.js"></script>
</head>
<body>
<div class="main ui text container">
<h1 class="ui dividing centered header">Popular Products</h1>
<div id="content"></div>
</div>
<script src="./js/seed.js"></script>
<script
type = "text/babel"
data-plugins="transform-class-properties"
src="./js/app.js"></script>
</body>
</html>
app.js
代码语言:javascript复制class ProductList extends React.Component {
render() {
return (
<div className='ui unstackable items'>
<Product/> {/*子组件*/}
</div>
);
}
}
class Product extends React.Component {
render() {
return (
<div className='item'>
<div className='image'>
<img src='images/products/image-aqua.png'/>
</div>
<div className='middle aligned content'>
<div className='description'>
<a>Fort Knight</a>
<p>Authentic renaissance actors, delivered in just two weeks.</p>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src='images/avatars/daniel.jpg'
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<ProductList/>, // 渲染的组件
document.getElementById('content')
// 渲染的组件位置 index.html 里的 id=content 的组件
)
2. 动态组件
- 数据驱动的组件,数据从父组件 流向 子组件,是通过
props
实现的
JSX属性值必须由 {} or ""
分隔
class ProductList extends React.Component {
render() {
const product = Seed.products[0];
return (
<div className="ui unstackable items">
<Product
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitterAvatarUrl={product.submitterAvatarUrl}
productImageUrl={product.productImageUrl}
/>
</div>
);
}
}
class Product extends React.Component {
render() {
return (
<div className='item'>
<div className='image'>
<img src={this.props.productImageUrl}/>
</div>
<div className='middle aligned content'>
<div className='header'>
<a>
<i className='large caret up icon'/>
</a>
{this.props.votes}
</div>
<div className='description'>
<a href={this.props.url}>
{this.props.title}
</a>
<p>
{this.props.description}
</p>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src={this.props.submitterAvatarUrl}
/>
</div>
</div>
</div>
);
}
}
3. 渲染多个组件
- 使用 map 函数,对多个组件进行处理
class ProductList extends React.Component {
render() {
const productComponents = Seed.products.map(
(product) => (
<Product
key={'product-' product.id}
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitterAvatarUrl={product.submitterAvatarUrl}
productImageUrl={product.productImageUrl}
/>
)
);
return (
<div className="ui unstackable items">
{productComponents}
</div>
)
}
}
- 排序 sort
const products = Seed.products.sort(
(a, b) => (b.votes - a.votes)
);
const productComponents = products.map......
按照投票数从上到下降序排列
sort 方法改变了原始数组,是一种危险的行为,需要小心bug
4. 事件响应
- 子组件可以读取其 props ,但是无法修改,props 是属于父组件的
- 父组件拥有子组件的 props
可以将 函数 作为 props 传递给 子组件
代码语言:javascript复制class ProductList extends React.Component {
handleProductUpVote(productId){
console.log(productId ' was upvoted.');
}
。。。
<Product
key={'product-' product.id}
id={product.id}
。。。
onVote={this.handleProductUpVote}
/>
class Product extends React.Component {
constructor(props) { // 构造函数
super(props);
this.handleUpVote = this.handleUpVote.bind(this);
// 自定义组件方法,需要手动将 this 绑定到自己的组件
}
handleUpVote() {
this.props.onVote(this.props.id);
}
render() {
return (
。。。
<a onClick={this.handleUpVote}>
<i className='large caret up icon'/>
</a>
)
- 自定义组件方法,需要手动将 this 绑定到自己的组件
- render 等函数,React 自动帮我们把 this 绑定到当前组件
可以看到控制台 (F12打开),输出了字符
5. 更新数据
this.state
是组件私有的,用this.setState()
更改,组件 state 或 props 更新时,组件会重新渲染- 不要在
this.setState()
之外的地方修改state
!!!因为这个函数 是异步的,我们不知道它什么时候更新状态 并 重新渲染
map()
,数组的 concat()
,不改变原数组,产生新的数组
如果想要修改,请修改副本,而不是原始对象
代码语言:javascript复制class ProductList extends React.Component {
constructor(props) {
super(props);
this.state = {
products: []
};
this.handleProductUpVote = this.handleProductUpVote.bind(this);
}
componentDidMount() { // 组件挂载到页面后,react会调用该方法
this.setState({products: Seed.products});
}
handleProductUpVote(productId){
const nextProducts = this.state.products.map(
(product) => {
if(product.id === productId) {
return Object.assign({}, product, {
votes: product.votes 1,
});
}
else{
return product;
}
}
);
this.setState({products: nextProducts});
}
render() {
const products = this.state.products.sort(
(a, b) => (b.votes - a.votes)
);
const productComponents = products.map(
(product) => (
<Product
key={'product-' product.id}
id={product.id}
title={product.title}
description={product.description}
url={product.url}
votes={product.votes}
submitterAvatarUrl={product.submitterAvatarUrl}
productImageUrl={product.productImageUrl}
onVote={this.handleProductUpVote}
/>
)
);
return (
<div className="ui unstackable items">
{productComponents}
</div>
)
}
}
class Product extends React.Component {
constructor(props) {
super(props);
this.handleUpVote = this.handleUpVote.bind(this);
}
handleUpVote() {
this.props.onVote(this.props.id);
}
render() {
return (
<div className='item'>
<div className='image'>
<img src={this.props.productImageUrl}/>
</div>
<div className='middle aligned content'>
<div className='header'>
<a onClick={this.handleUpVote}>
<i className='large caret up icon'/>
</a>
{this.props.votes}
</div>
<div className='description'>
<a href={this.props.url}>
{this.props.title}
</a>
<p>
{this.props.description}
</p>
</div>
<div className='extra'>
<span>Submitted by:</span>
<img
className='ui avatar image'
src={this.props.submitterAvatarUrl}
/>
</div>
</div>
</div>
);
}
}
ReactDOM.render(
<ProductList/>, // 渲染的组件
document.getElementById('content')
// 渲染的组件位置 index.html 里的 id=content 的组件
)
由于我们使用了插件 transform-class-properties
(属性初始化器)
- 可以写箭头函数来自定义组件方法,直接绑定 this 到组件
- 在
constructor()
函数之外定义初始状态
<script
type = "text/babel"
data-plugins="transform-class-properties"
src="./js/app.js"></script>
所以,可以这么写:
代码语言:javascript复制class Product extends React.Component {
// constructor(props) {
// super(props);
// this.handleUpVote = this.handleUpVote.bind(this);
// }
// handleUpVote() {
// this.props.onVote(this.props.id);
// }
handleUpVote = ()=>(
this.props.onVote(this.props.id)
)
代码语言:javascript复制class ProductList extends React.Component {
// constructor(props) {
// super(props);
// this.state = {
// products: []
// };
// this.handleProductUpVote = this.handleProductUpVote.bind(this);
// }
state = {products: []}; // 在 `constructor()` 函数之外定义初始状态
handleProductUpVote = (productId) => {。。。}