简介
React.js 不是一个框架,它只是一个库。它只提供 UI (view)层面的解决方案。在实际的项目当中,它并不能解决我们所有的问题,需要结合其它的库,例如 Redux、React-router 等来协助提供完整的解决方法。
render () {
return (
<div>
<h1>React 小书 {(function () { return 'is good'})()}</h1>
</div>
)
}
注意,直接使用 class 在 React.js 的元素上添加类名如
还有一个特例就是 for 属性,例如 Male,因为 for 也是 JavaScript 的关键字,所以在 JSX 用 htmlFor 替代,即 Male。而其他的 HTML 属性例如 style 、data-* 等就可以像普通的 HTML 属性那样直接添加上去。
render () {
const isGoodWord = true
return (
<div>
<h1>
React 小书
{isGoodWord
? <strong> is good</strong>
: <span> is not good</span>
}
</h1>
</div>
)
}
如果你在表达式插入里面返回 null ,那么 React.js 会什么都不显示,相当于忽略了该表达式插入。
renderGoodWord (goodWord, badWord) {
const isGoodWord = true
return isGoodWord ? goodWord : badWord
}
render () {
return (
<div>
<h1>
React 小书
{this.renderGoodWord(
<strong> is good</strong>,
<span> is not good</span>
)}
</h1>
</div>
)
}
组件组合
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class Title extends Component {
render () {
return (
<h1>React 小书</h1>
)
}
}
class Header extends Component {
render () {
return (
<div>
<Title />
<h2>This is Header</h2>
</div>
)
}
}
class Main extends Component {
render () {
return (
<div>
<h2>This is main content</h2>
</div>
)
}
}
class Footer extends Component {
render () {
return (
<div>
<h2>This is footer</h2>
</div>
)
}
}
class Index extends Component {
render () {
return (
<div>
<Header />
<Main />
<Footer />
</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
组件标签使用大写字母开头
事件触发例子
class Title extends Component {
handleClickOnTitle () {
console.log('Click on title.')
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>React 小书</h1>
)
}
}
这些 on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。也就是说,
这样的写法不会有什么效果的,这些 on* 的事件监听只能用在普通的 HTML 的标签上,而不能用在组件标签上。
事件触发方法内调用
传入一个event 对象
Event 对象代表事件的状态,比如事件在其中发生的元素、键盘按键的状态、鼠标的位置、鼠标按钮的状态。
事件通常与函数结合使用,函数不会在事件发生前被执行!
class Title extends Component {
handleClickOnTitle (e) {
console.log(e.target.innerHTML)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle}>React 小书</h1>
)
}
}
事件中使用当前实例,在事件方法中this是不会返回当前实例的,可以使用:
class Title extends Component {
handleClickOnTitle (e) {
console.log(this)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle.bind(this)}>React 小书</h1>
)
}
}
这种 bind 模式在 React.js 的事件监听当中非常常见,bind 不仅可以帮我们把事件监听方法中的 this 绑定到当前组件实例上;还可以帮助我们在在渲染列表元素的时候,把列表元素传入事件监听函数。
bind() 方法创建一个新的函数,在 bind() 被调用时,这个新函数的 this 被指定为 bind() 的第一个参数,而其余参数将作为新函数的参数,供调用时使用。
class Title extends Component {
handleClickOnTitle (word, e) {
console.log(this, word)
}
render () {
return (
<h1 onClick={this.handleClickOnTitle.bind(this, 'Hello')}>React 小书</h1>
)
}
}
向事件处理程序传递参数
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
分别通过箭头函数和 Function.prototype.bind 来实现。
关于setState()更新状态信息
setState 方法由父类 Component 所提供。当我们调用这个函数的时候,React.js 会更新组件的状态 state ,并且重新调用 render 方法,然后再把 render 方法所渲染的最新的内容显示到页面上。
constructor (props) {
super(props)
this.state = {
name: 'Tomy',
isLiked: false
}
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
当你调用 setState 的时候,React.js 并不会马上修改 state。而是把这个对象放到一个更新队列里面,稍后才会从队列当中把新的状态提取出来合并到 state 当中,如果你想在 setState 之后使用新的 state 来做后续运算就做不到了
第二种使用方式可以解决这个问题,React.js 会把上一个 setState 的结果传入这个函数,你就可以使用该结果进行运算、操作,然后返回一个对象作为更新 state 的对象:
handleClickOnLikeButton () {
this.setState((prevState) => {
return { count: 0 }
})
this.setState((prevState) => {
return { count: prevState.count + 1 } // 上一个 setState 的返回是 count 为 0,当前返回 1
})
this.setState((prevState) => {
return { count: prevState.count + 2 } // 上一个 setState 的返回是 count 为 1,当前返回 3
})
// 最后的结果是 this.state.count 为 3
}
上面我们进行了三次 setState,但是实际上组件只会重新渲染一次,而不是三次;
可配置性
通过props来进行配置
class LikeButton extends Component {
constructor () {
super()
this.state = { isLiked: false }
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
const likedText = this.props.likedText || '取消'
const unlikedText = this.props.unlikedText || '点赞'
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked ? likedText : unlikedText} 👍
</button>
)
}
}
设定属性
class Index extends Component {
render () {
return (
<div>
<LikeButton likedText='已赞' unlikedText='赞' />
</div>
)
}
}
<LikeButton wordings={{likedText: '已赞', unlikedText: '赞'}} />
传入方法
class Index extends Component {
render () {
return (
<div>
<LikeButton
wordings={{likedText: '已赞', unlikedText: '赞'}}
onClick={() => console.log('Click on like button!')}/>
</div>
)
}
}
调用方法
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
if (this.props.onClick) {
this.props.onClick()
}
}
默认参数
class LikeButton extends Component {
static defaultProps = {
likedText: '取消',
unlikedText: '点赞'
}
constructor () {
super()
this.state = { isLiked: false }
}
handleClickOnLikeButton () {
this.setState({
isLiked: !this.state.isLiked
})
}
render () {
return (
<button onClick={this.handleClickOnLikeButton.bind(this)}>
{this.state.isLiked
? this.props.likedText
: this.props.unlikedText} 👍
</button>
)
}
}
如果没有传进来,会直接使用 defaultProps 中的默认属性。
props 一旦传入进来就不能改变
你不能改变一个组件被渲染的时候传进来的 props。React.js 希望一个组件在输入确定的 props 的时候,能够输出确定的 UI 显示形态。如果 props 渲染过程中可以被修改,那么就会导致这个组件显示形态和行为变得不可预测,这样会可能会给组件使用者带来困惑。
组件的使用者可以主动地通过重新渲染的方式把新的 props 传入组件当中,这样这个组件中由 props 决定的显示形态也会得到相应的改变。
不允许修改的方法
handleClickOnLikeButton () {
this.props.likedText = '取消'
}
允许修改的方法
handleClickOnChange () {
this.setState({
likedText: '取消',
unlikedText: '点赞'
})
}
由于 setState 会导致 Index 重新渲染,所以 LikedButton 会接收到新的 props,并且重新渲染,于是它的显示形态也会得到更新。这就是通过重新渲染的方式来传入新的 props 从而达到修改 LikedButton 显示形态的效果。
state 是让组件控制自己的状态,props 是让外部对组件自己进行配置。
列表渲染
const users = [
{ username: 'Jerry', age: 21, gender: 'male' },
{ username: 'Tomy', age: 22, gender: 'male' },
{ username: 'Lily', age: 19, gender: 'female' },
{ username: 'Lucy', age: 20, gender: 'female' }
]
class Index extends Component {
render () {
const usersElements = [] // 保存每个用户渲染以后 JSX 的数组
for (let user of users) {
usersElements.push( // 循环每个用户,构建 JSX,push 到数组中
<div>
<div>姓名:{user.username}</div>
<div>年龄:{user.age}</div>
<div>性别:{user.gender}</div>
<hr />
</div>
)
}
return (
<div>{usersElements}</div>
)
}
}
ReactDOM.render(
<Index />,
document.getElementById('root')
)
简化写法
class Index extends Component {
render () {
return (
<div>
{users.map((user) => {
return (
<div>
<div>姓名:{user.username}</div>
<div>年龄:{user.age}</div>
<div>性别:{user.gender}</div>
<hr />
</div>
)
})}
</div>
)
}
}
对于用表达式套数组罗列到页面上的元素,都要为每个元素加上 key 属性,这个 key 必须是每个元素唯一的标识。
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用数据中的 id 来作为元素的 key,当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
在 map() 方法中的元素需要设置 key 属性。
构造函数
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
}
生命周期方法
componentDidMount() 方法会在组件已经被渲染到 DOM 中后运行
一旦 Clock 组件从 DOM 中被移除,React 就会调用 componentWillUnmount() 生命周期方法
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('root')
);
根据单一功能原则来判定组件的范围。也就是说,一个组件原则上只能负责一个功能。如果它需要负责更多的功能,这时候就应该考虑将它拆分成更小的组件。
参考文章: