使用 ES6 class 声明,可不严格遵守该属性声明次序
但如有 propTypes 则必须写在顶部, lifecycle events 必须写到一起
数组遍历,map,必须要用 key属性
在一个函数里面,改变传入的对象本身是不好的写法
- propTypes
- defaultPropTypes
- constructor
- event handlers (如不使用类属性语法可在此声明)
- event handlers (如不使用类属性语法可在此声明)
- lifecycle events
- event handlers
- getters
render
class App extends React.Component {
static propTypes = {
firstName: PropTypes.string.isRequired,
lastName: PropTypes.string.isRequired
}
static defaultPropTypes = {
firstName: '',
lastName: '',
}
constructor(props) {
super(props)
this.state = { visible: false }
/* 若不能使用 babel-plugin-transform-class-properties
this.handleClick = () => {
this.setState({visible: !this.state.visible})
}
*/
}
componentWillMount() { }
componentDidMount() { }
handleClick = () => {
this.setState({ visible: !this.state.visible })
}
get fullName() {
const { firstName, lastName } = this.props
return `${firstName} ${lastName}`
}
render() {
return (
<div onClick={this.handleClick}>
{this.fullName} {this.state.visible ? 'is visible' : ''}
</div>
)
}
}
get计算属性
使用 getters 封装 render 所需要的状态或条件的组合
对于返回 boolean 的 getter 使用 is- 前缀命名,例如 isVisible
class App extends React.Component {
get isVIP() {
const { age } = this.state
const { level } = this.props
const vips = ['A', 'B']
return age > 18 && (vips.includes(level))
}
render() {
return (
<div>
{this.isVIP ? <VipComponent /> : <NormalComponent />}
</div>
)
}
}
不推荐的用法
// bad
render () {
return (
<div>
{
this.state.age > 18
&& (this.props.level === 'A'
|| this.props.level === 'B')
? <VipComponent />
: <NormalComponent />
}
</div>
)
}
事件回调命名规范
Handler 命名风格:
- 使用
handle
开头,使用一般现在时 - 以事件类型作为结尾 (如
Click
,Change
) - handleClick,handleChange ```jsx closeAll = () => {},
// good render () { return
}// bad render () { return
}
如果你需要区分同样事件类型的 handler(如 `handleNameChange` 和 `handleEmailChange`)时,<br />可能这就是一个拆分组件的信号
<a name="lLQjn"></a>
## 组件化代替多个render
当组件的 jsx 只写在一个 render 方法显得太臃肿时,就拆分出一个个小组件<br />采用 class component 或 stateless component 无状态组件
Table表格组件,用的 render方法比较多
- renderAction
- renderStatus
- RenderTime
```jsx
class App extends React.Component {
render() {
return (
<div className="menu">
<ul>
{this.props.items.map(item => <Items {...item} />)}
</ul>
</div>
)
}
}
// Items子组件负责渲染逻辑
function Items ({name}) {
return (
<li>
{name}
{/* ... */}
</li>
)
}
不推荐的写法
// bad
class App extends React.Component {
renderItem({ name }) {
return (
<li>
{name}
{/* ... */}
</li>
)
}
render() {
return (
<div className="menu">
<ul>
{this.props.items.map(item => this.renderItem(item))}
</ul>
</div>
)
}
}
state状态提升
state状态上移优于公共方法
把公共数据放在父组件里面
Lifting State Up https://reactjs.org/docs/lifting-state-up.html
更细颗粒的组件化,状态应集中在远离渲染的地方处理,
- 比如应用级别的状态就在 redux 的 store 里
- 也能使兄弟组件更方便地共享
- 一般组件不应提供公共方法,这样会破坏数据流只有一个方向的原则 ```jsx // parent import DropDownMenu from ‘./DropDownMenu’
class App extends React.Component { constructor (props) { super(props) this.state = { visible: false } }
showMenu () { this.setState({visible: true}) }
hideMenu () { this.setState({visible: false}) }
render () {
return
child子组件
```jsx
class DropDownMenu extends Component {
static propsType = {
visible: PropTypes.boolean.isRequired
}
render () {
if(!this.props.visible) return null
return (
<div className="dropdown-menu">
{/* ... */}
</div>
)
}
}
不推荐的写法
class App extends React.Component {
showMenu () {
this.refs.menu.show()
}
hideMenu () {
this.refs.menu.hide()
}
render () {
return <DropDownMenu ref="menu" />
}
}
// child 子组件
class DropDownMenu extends Component {
constructor (props) {
super(props)
this.state = {
visible: false
}
}
show () {
this.setState({visible: true})
}
hide () {
this.setState({visible: false})
}
render () {
return this.state.visible && (
<div className="dropdown-menu">
{/* ... */}
</div>
)
}
}
容器组件
容器组件主要负责 维护状态和 数据的计算
- 本身并没有界面逻辑,只把结果通过 props 传递下去
- 把组件的状态和渲染解耦开来
改写界面时不用关注数据的实现,顺便得到了可复用性 ```jsx class MessageContainer extends Component { constructor(props) { super(props) this.state = {
onlyUnread: false,
messages: []
} }
// 父组件获取数据,处理副作用 componentDidMount() { $.ajax({
url: "/api/messages",
}).then(({ messages }) => this.setState({ messages })) }
handleClick = () => this.setState({ onlyUnread: !this.state.onlyUnread })
render() { return <MessageList
messages={this.state.messages.filter(msg => this.state.onlyUnread ? !msg.asRead : true)}
toggleUnread={this.handleClick}
/> } }
// child子组件 MessageList.propTypes = { messages: propTypes.array.isRequired, toggleUnread: propTypes.func.isRequired }
function MessageList({ messages, toggleUnread }) { return (
) }
bad,不推荐的写法,<br />组件的状态和渲染没有分开
```jsx
class MessageList extends Component {
constructor (props) {
super(props)
this.state = {
onlyUnread: false,
messages: []
}
}
componentDidMount () {
$.ajax({
url: "/api/messages",
}).then(({messages}) => this.setState({messages}))
}
handleClick = () => this.setState({onlyUnread: !this.state.onlyUnread})
render () {
return (
<div class="message">
<ul>
{
this.state.messages
.filter(msg => this.state.onlyUnread ? !msg.asRead : true)
.map(({content, author}) => {
return <li>{content}—{author}</li>
})
}
</ul>
<button onClick={this.handleClick}>toggle unread</button>
</div>
)
}
}