- props 传递数据
- props 传递函数
- 以属性方式传递数据和函数
- 函数组价默认没有 state
- props 类型检查
- defaultProps 推荐使用静态属性定义,不推荐在 class 外进行定义
- 在react中可以定义默认props,使用es5时,可以使用getDefaultProps
const App = React.createClass({
getDefaultProps: function() {
return {
name: 'demo'
}
},
render: function() {
return <h1>This is my {this.props.name}</h1>
}
})
class组件模板
- propTypes & defaultProps
- 所有的组件都应该有prop types
propTypes
和defaultProps
是静态属性 static- 尽可能在组件类的的前面定义,让其他的开发人员读代码的时候可以立刻注意到。起到文档的作用
- 所有的组件都应该有prop types
- 箭头函数会自动维护正确的上下文, this
- 类方法
- 类属性
- 类方法
- this.setState 默认是异步的,推荐使用函数的写法
this.setState((state, props)=> ({ }))
- react会把多次调用的
setState
放在一起调用 - 调用了
setState
之后state不一定会立即改变,函数可以获取最新的 state的值,推荐使用函数写法
- 本地引入的import 和全局的 import会用一个空行来分割
- const 写在 import的后面
- 先引入全局的 import,后引入 本地的 import
- const 写在 import的后面
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import styles from './style.scss'
class Parent extends Component {
state = { // 设置初始状态
visible: false
}
static propTypes = {
title: PropTypes.string,
data: PropTypes.array.isRequired,
}
static defaultProps = {
data: [],
title: ''
}
handleChange = (e) => {
e.preventDefault()
const value = e.target.value
this.props.onClick(value)
}
handleClick = () => {
this.setState(state => ({visible: !state.visible }))
}
render () {
// 多行的props的,每一个prop都应该单独占一行,Prettier 格式化
const {
data,
title
} = this.props
return (
<div>
<h1>{title}</h1>
</div>
)
}
}
export default Parent
装饰器语法
- 装饰器语法 Decorator
- 装饰器就是把类组件作为一个参数传入了一个方法
- https://javascript.info/call-apply-decorators
```jsx import React, { Component } from ‘react’ import PropTypes from ‘prop-types’ import { observer } from ‘mobx-react’
import styles from ‘./style.scss’
@observer class Parent extends Component {}
export default Parent
// 不用装饰器的写法 class Parent extends Component {} export default observer(Parent)
<a name="CPVzU"></a>
### 子组件
1. 避免在子组件中传入闭包 Closures<br />
2. 原因:每次父组件 render 的时候,都会新建一个新的函数并传递给 input
1. 如果`input`是一个React组件,会自动触发组件的重绘 re-render,不管其他的props是否发生了改变<br />
```jsx
handleChange = (e) => {
const value = e.target.value
this.setState(() => ({ value }))
}
<Input
value=""
onChange={ e => this.handleChange(e) }
// onChange={ e => this.setState({ value: e.target.value }) } // 闭包,不推荐的写法
/>
函数组件
- Functional Components 没有state,没有props,也没有方法,是一个纯函数的组件
- 在组件的声明之前就定义了
propTypes
- 组件就是一个方法(函数),参数就是
props
- 函数组件没法使用装饰器,只能把函数作为参数传入别的函数里面
- UI组件和容器组件
import React from 'react'
import PropTypes from 'prop-types'
import { observer } from 'mobx-react'
import styles from './styles.css'
Child.propTypes = {
expanded: PropTypes.bool,
onSubmit: PropTypes.func.isRequired,
onExpand: PropTypes.func.isRequired
}
function Child (props) {
const { children, onSubmit, onExpand, expand = false } = props
return (
<form style={styles.form} onSubmit={onSubmit}>
{children}
<button onClick={onExpand}>Expand</button>
</form>
)
}
// 代替装饰器语法:把函数作为参数传入到其他的函数里面
export default observer(Child)
const匿名函数
- 如果Babel配置错误,组件里的任何错误都显示为发生在 <
>里的,调试起来就非常麻烦 - 匿名方法也会引起 Jest其他的问题。由于会引起各种难以理解的问题,推荐使用
function
,少使用const
```jsx // 推荐的用法 function Child (props) {}
// 不推荐的用法 const Child = ({ onExpand, expanded, children }) => {}
<a name="65gFa"></a>
### 多个if判断
1. 多个 if条件判断,根据不同的条件渲染不同组件,用匿名函数自执行,来达到使用 if...else 的目的<br />
2. `{ (() => {})() }`<br />
3. 用大括号包起来的[IIFE] 匿名函数自执行,然后把`if`表达式都放进去。返回你要返回的组件
1. 大量使用立即执行函数会造成性能上的损失<br />
```jsx
// { (() => {})() }
<div>
{
(() => {
if (visible) {
return <h1></h1>
}
else {
return <Empty />
}
})()
}
</div>
es6 constructor初始化
import React from 'react'
import PropTypes from 'prop-types'
export default class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.count}
}
onClick() {
this.setState(state => ({count: state.count + 1}) );
}
render() {
return (
<div onClick={this.onClick.bind(this)}>
Clicks: {this.state.count}
</div>
);
}
}
Parent.propTypes = { count: PropTypes.number };
Parent.defaultProps = { count: 0 }
es7初始化
- 推荐使用 es7的语法
- ES7 is better because it enables the following scenarios
- https://stackoverflow.com/questions/35662932/react-constructor-es6-vs-es7
- ES7 is better because it enables the following scenarios
- es7 class语法
- 用 redux时,要 export defaut redux
import React from 'react'
import PropTypes from 'prop-types'
export default class Parent extends React.Component {
static propTypes = { count: PropTypes.number }
static defaultProps = { count: 0 }
state = { count: this.props.count }
onClick = () => {
this.setState(state => ({ count: state.count + 1 }) );
}
render() {
return (
<div onClick={this.onClick}>
Clicks: {this.state.count}
</div>
)
}
}
class组件
- class 必须继承 React.Component 或 React.PureComponent
- class内部必须有 render方法
- render方法返回 jsx
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'
import './index.less'
class Todo extends PureComponent {
constructor (props) {
super(props)
this.state = {
user: ''
}
}
// 默认 props参数类型约束
static propTypes = {
className: PropTypes.string,
style: PropTypes.object,
data: PropTypes.array,
allowClear: PropType.bool
}
// 默认 props参数
static defaultProps = {
className: '',
style: {},
data: [],
allowClear: true
}
// 推荐使用静态方法来绑定事件
handleChange = (id, title, ev) => {
// this 默认 undefined
}
render () {
const { data, fetching, className, style, ...attr } = this.props
const loading = fetching ? <Spin size="small" /> : ''
const cls = classnames('loading', {
[className]: true,
})
return (
<>
<header onClick="this.handleClick">Parent</header>
</>
)
}
}
props
- props来进行父子组件传参
- 父组件调用子组件时传入属性
- 子组件直接通过
this.props.属性名
拿到父组件传过来的值
- 子组件直接通过
- 在父组件里定义一个函数,调用子组件时将函数传过去
- 子组件通过
this.props.函数名()
来调用函数并且执行 - prop-types 对父级传参进行验证
- 子组件通过
默认 props
- 设置静态属性 static defaultProps,默认的 props参数
- 只能在 class组件中设置
class MyDemo extends React.PureComponent{
constructor(props){
super(props)
}
//如果babel设置为es6的转码方式会报错,ES6的class中只有静态方法,没有静态属性。
// 因为定义静态属性不属于es6,而在es7的草案中。
static defaultProps = { // props 默认值
user:"默认值"
}
render(){
return <h1>This is {this.props.name}</h1>
}
}
static 静态属性
- 如果babel设置为es6的转码方式,会报错
- 因为定义静态属性不属于es6,而在es7的草案中
- ES6的class中只有静态方法,没有静态属性
- 由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性
- class的属性只能定义在class之外。所以defaultProps要写在组件外部
```jsx
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return
This is my {this.props.name}
} }
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。 App.defaultProps = { name: ‘demo’ }
<br />
<a name="R65oh"></a>
## class组件优化
1. class类组件中,只要调用setState就会触发render
2. 父组件state改变时,如果子组件接受的参数并没有变化,那么子组件是不会重新渲染的<br />
1. 子组件根本没有接收参数,自然也不会产生性能优化问题;
1. 所以,子组件传值,不要多传值,不然只要这个值改变就会触发重新渲染<br />
2. 在传递props/state时,只传递需要的参数<br />
2. 子组件通过 `componentWillReceiveProps` 生命周期方法,将接收的 props转换为子组件的 state,不推荐使用<br />
3. hooks函数组件 memo的第二个参数
1. 上一次的 props 和 这一次 props,返回true/false<br />
<br />
<a name="7f612e76"></a>
## webpack配置
1. 如果使用webpack的话,加入stage-0后就能尝试es7语法了,static也能在class内部定义属性
```jsx
loaders:[
{
test: /\.js[x]?$/,
exclude: /node_modules/,
loader: 'babel-loader' ,
query:{
presets:['es2015','stage-0','react'],
plugins:['transform-runtime']
},
}
]
- 如果报错,因为static本是es7中的草案,要想使用static需要做一些配置
- 首先,确保安装了babel-preset-react、babel-core、babel-loader、babel-preset-es2015
- 如果没有安装,就执行如下语句 ```jsx npm install babel-core babel-loader babel-preset-es2015 babel-preset-react —save-dev
// 然后安装 babel-preset-stage-0 es7草案语法 npm install —save-dev babel-preset-stage-0
// 之后配置webpack.config.js let path = require(‘path’); let webpack = require(‘webpack’);
module.exports = { entry: dirname+’/src/entry.js’, output: { path: dirname+’/dist’, filename: ‘bundle.js’ }, devtool: ‘source-map’, resolve: {//指定可以被import的文件名后缀 extensions: [‘.js’, ‘.jsx’,’.sass’,’.ts’] }, module: { loaders: [ { test: /.js?$/, exclude: /(node_modules|bower_components)/, loader: ‘babel-loader’, query: { presets: [‘es2015’, ‘react’,’stage-0’] } //将react编译成js文件 }, { test: /.css$/, loader: ‘style-loader!css-loader’ }, //打包css文件 { test: /.scss$/, loader: ‘style!css!sass?sourceMap’}, //编译sass文件 { test: /.(png|jpg)$/, loader: ‘url-loader?limit=8192’} //对图片进行打包 ] }, //4、服务器依赖包配置 devServer: {//注意:网上很多都有colors属性,但是实际上的webpack2.x已经不支持该属性了 contentBase: “./dist”,//本地服务器所加载的页面所在的目录 historyApiFallback: true,//不跳转 inline: true//实时刷新 } } ```