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:\Users\user\AppData\Roaming\npm
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
<!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><scripttype = "text/babel"data-plugins="transform-class-properties"src="./js/app.js"></script></body></html>
app.js
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><imgclassName='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"><Productid={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><imgclassName='ui avatar image'src={this.props.submitterAvatarUrl}/></div></div></div>);}
}
3. 渲染多个组件
- 使用 map 函数,对多个组件进行处理
class ProductList extends React.Component {render() {const productComponents = Seed.products.map((product) => (<Productkey={'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 传递给 子组件
class ProductList extends React.Component {handleProductUpVote(productId){console.log(productId + ' was upvoted.');}。。。<Productkey={'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()
,不改变原数组,产生新的数组
如果想要修改,请修改副本,而不是原始对象
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) => (<Productkey={'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><imgclassName='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()
函数之外定义初始状态
<scripttype = "text/babel"data-plugins="transform-class-properties"src="./js/app.js"></script>
所以,可以这么写:
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))
class ProductList extends React.Component {// constructor(props) {// super(props);// this.state = {// products: []// };// this.handleProductUpVote = this.handleProductUpVote.bind(this);// }state = {products: []}; // 在 `constructor()` 函数之外定义初始状态handleProductUpVote = (productId) => {。。。}