- 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.valuethis.props.onClick(value)}handleClick = () => {this.setState(state => ({visible: !state.visible }))}render () {// 多行的props的,每一个prop都应该单独占一行,Prettier 格式化const {data,title} = this.propsreturn (<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 的时候,都会新建一个新的函数并传递给 input1. 如果`input`是一个React组件,会自动触发组件的重绘 re-render,不管其他的props是否发生了改变<br />```jsxhandleChange = (e) => {const value = e.target.valuethis.setState(() => ({ value }))}<Inputvalue=""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 } = propsreturn (<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.propsconst 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就会触发render2. 父组件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内部定义属性```jsxloaders:[{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//实时刷新 } } ```
