class Counter extends React.Component{ // 他会比较两个状态相等就不会刷新视图 PureComponent是浅比较
static defaultProps = {
name:'珠峰培训'
};
constructor(props){
super();
this.state = {number:0}
console.log('1.constructor构造函数')
}
componentWillMount(){ // 取本地的数据 同步的方式:采用渲染之前获取数据,只渲染一次
console.log('2.组件将要加载 componentWillMount');
}
componentDidMount(){
console.log('4.组件挂载完成 componentDidMount');
}
handleClick=()=>{
this.setState({number:this.state.number+1});
};
// react可以shouldComponentUpdate方法中优化 PureComponent 可以帮我们做这件事
shouldComponentUpdate(nextProps,nextState){ // 代表的是下一次的属性 和 下一次的状态
console.log('5.组件是否更新 shouldComponentUpdate');
return nextState.number%2;
// return nextState.number!==this.state.number; //如果此函数种返回了false 就不会调用render方法了
} //不要随便用setState 可能会死循环
componentWillUpdate(){
console.log('6.组件将要更新 componentWillUpdate');
}
componentDidUpdate(){
console.log('7.组件完成更新 componentDidUpdate');
}
render(){
console.log('3.render');
return (
<div>
<p>{this.state.number}</p>
{this.state.number>3?null:<ChildCounter n={this.state.number}/>}
<button onClick={this.handleClick}>+</button>
</div>
)
}
}
class ChildCounter extends Component{
componentWillUnmount(){
console.log('组件将要卸载componentWillUnmount')
}
componentWillMount(){
console.log('child componentWillMount')
}
render(){
console.log('child-render')
return (<div>
{this.props.n}
</div>)
}
componentDidMount(){
console.log('child componentDidMount')
}
componentWillReceiveProps(newProps){ // 第一次不会执行,之后属性更新时才会执行
console.log('child componentWillReceiveProps')
}
shouldComponentUpdate(nextProps,nextState){
return nextProps.n%3; //子组件判断接收的属性 是否满足更新条件 为true则更新
}
}
// defaultProps
// constructor
// componentWillMount
// render
// componentDidMount
// 状态更新会触发的
// shouldComponentUpdate nextProps,nextState=>boolean
// componentWillUpdate
// componentDidUpdate
// 属性更新
// componentWillReceiveProps newProps
// 卸载
// componentWillUnmount

React 内置了类型检测的功能。要在组件中进行类型检测,你可以赋值 propTypes 属性
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Person extends React.Component{
static defaultProps = {
name:'Stranger'
}
static propTypes={
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired,
gender: PropTypes.oneOf(['male','famale']),
hobby: PropTypes.array,
postion: PropTypes.shape({
x: PropTypes.number,
y:PropTypes.number
}),
age(props,propName,componentName) {
let age=props[propName];
if (age <0 || age>120) {
return new Error(`Invalid Prop ${propName} supplied to ${componentName}`)
}
}
}
render() {
let {name,age,gender,hobby,position}=this.props;
return (
<table>
<thead>
<tr>
<td>姓名</td>
<td>年龄</td>
<td>性别</td>
<td>爱好</td>
<td>位置</td>
</tr>
</thead>
<tbody>
<tr>
<td>{name}</td>
<td>{age}</td>
<td>{gender}</td>
<td>{hobby.join(',')}</td>
<td>{position.x+' '+position.y}</td>
</tr>
</tbody>
</table>
)
}
}
let person={
age: 100,
gender:'male',
hobby: ['basketball','football'],
position: {x: 10,y: 10},
}
ReactDOM.render(<Person {...person}/>, document.getElementById('root'));
在内部,React使用几种巧妙的技术来最大限度地减少更新UI所需的昂贵的 DOM 操作的数量
Create React App
npm run build
单文件构建
<script src="https://unpkg.com/[email protected]/dist/react.min.js"></script>
<script src="https://unpkg.com/[email protected]/dist/react-dom.min.js"></script>
webpack
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
new webpack.optimize.UglifyJsPlugin()

import { is } from 'immutable';
shouldComponentUpdate: (nextProps, nextState) => {
return !(this.props === nextProps || is(this.props, nextProps)) ||
!(this.state === nextState || is(this.state, nextState));
}
改进setStatethis.setState({ data: this.state.data.update('counter', counter => counter + 1) });
当比较不同的两个树,React 首先比较两个根元素。根据根跟的类型不同,它有不同的行为
在某些场景下,你想在整个组件树中传递数据,但却不想手动地在每一层传递属性。你可以直接在 React 中使用强大的”context” API解决上述问题
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
class Header extends Component{
render() {
return (
<div>
<Title/>
</div>
)
}
}
class Title extends Component{
static contextTypes={
color:PropTypes.string
}
render() {
return (
<div style={{color:this.context.color}}>
Title
</div>
)
}
}
class Main extends Component{
render() {
return (
<div>
<Content>
</Content>
</div>
)
}
}
class Content extends Component{
static contextTypes={
color: PropTypes.string,
changeColor:PropTypes.func
}
render() {
return (
<div style={{color:this.context.color}}>
Content
<button onClick={()=>this.context.changeColor('green')}>绿色</button>
<button onClick={()=>this.context.changeColor('orange')}>橙色</button>
</div>
)
}
}
class Page extends Component{
constructor() {
super();
this.state={color:'red'};
}
static childContextTypes={
color: PropTypes.string,
changeColor:PropTypes.func
}
getChildContext() {
return {
color: this.state.color,
changeColor:(color)=>{
this.setState({color})
}
}
}
render() {
return (
<div>
<Header/>
<Main/>
</div>
)
}
}
ReactDOM.render(<Page/>,document.querySelector('#root'));
React 中一个常见模式是为一个组件返回多个元素。 片段(fragments) 可以让你将子元素列表添加到一个分组中,并且不会在DOM中增加额外节点。
import React from 'react';
import ReactDOM from 'react-dom';
class List extends React.Component{
render() {
return (
<React.Fragment>
{
this.props.todos.map(item => (<li>{item}</li>))
}
</React.Fragment>
)
}
}
class Todos extends React.Component{
constructor() {
super();
this.state={todos:['a','b','c']};
}
render() {
return (
<ul>
<List todos={this.state.todos}/>
</ul>
)
}
}
ReactDOM.render(<Todos/>,document.querySelector('#root'));
Portals 提供了一种很好的方法,将子节点渲染到父组件 DOM 层次结构之外的 DOM 节点。
ReactDOM.createPortal(child, container)
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import './modal.css';
class Modal extends Component{
constructor() {
super();
this.modal=document.querySelector('#modal-root');
}
render() {
return ReactDOM.createPortal(this.props.children,this.modal);
}
}
class Page extends Component{
constructor() {
super();
this.state={show:false};
}
handleClick=() => {
this.setState({show:!this.state.show});
}
render() {
return (
<div>
<button onClick={this.handleClick}>显示模态窗口</button>
{
this.state.show&&<Modal>
<div id="modal" className="modal">
<div className="modal-content" id="modal-content">
内容
<button onClick={this.handleClick}>关闭</button>
</div>
</div>
</Modal>
}
</div>
)
}
}
ReactDOM.render(<Page/>,document.querySelector('#root'));
.modal{
position: fixed;
left:0;
top:0;
right:0;
bottom:0;
background: rgba(0,0,0,.5);
display: block;
}
@keyframes zoom{
from{transform:scale(0);}
to{transform:scale(1);}
}
.modal .modal-content{
width:50%;
height:50%;
background: white;
border-radius: 10px;
margin:100px auto;
display:flex;
flex-direction: row;
justify-content: center;
align-items: center;
animation: zoom .6s;
}
部分 UI 中的 JavaScript 错误不应该破坏整个应用程序。 为了解决 React 用户的这个问题,React 16引入了一个 “错误边界(Error Boundaries)” 的新概念。
import React from 'react';
import ReactDOM from 'react-dom';
class ErrorBoundary extends React.Component{
constructor(props) {
super(props);
this.state={hasError:false};
}
componentDidCatch(err,info) {
this.setState({hasError: true});
}
render() {
if (this.state.hasError) {
return <h1>Something Went Wrong</h1>
}
return this.props.children;
}
}
class Page extends React.Component{
render() {
return (
<ErrorBoundary>
<Clock/>
</ErrorBoundary>
)
}
}
class Clock extends React.Component{
render() {
return (
<div>hello{null.toString()}</div>
)
}
}
ReactDOM.render(<Page/>,document.querySelector('#root'));
const NewComponent = higherOrderComponent(OldComponent)
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
export default (WrappedComponent,name) => {
class HighOrderComponent extends Component{
constructor() {
super();
this.state={data:null};
}
componentWillMount() {
let data=localStorage.getItem(name);
this.setState({data});
}
render() {
return <WrappedComponent data={this.state.data}/>
}
}
return HighOrderComponent;
}
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import wrapLocalData from './wrapLocalData';
let UserName=(props) => {
return <input type="text" defaultValue={props.data}/>
}
let Password=(props) => {
return <input type="text" defaultValue={props.data}/>
}
let NewUserName=wrapLocalData(UserName,'username');
let NewPassword=wrapLocalData(Password,'password');
class Form extends Component{
render() {
return (
<form>
<NewUserName />
<NewPassword/>
</form>
)
}
}
ReactDOM.render(<Form/>,document.querySelector('#root'));
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
export default (WrappedComponent,name) => {
class HighOrderComponent extends Component{
constructor() {
super();
this.state={data:null};
}
componentWillMount() {
fetch('/data.json',{
method: 'GET'
}).then(response => response.json()).then(data => {
console.log(data[name]);
this.setState({data:data[name]});
})
}
render() {
return <WrappedComponent data={this.state.data}/>
}
}
return HighOrderComponent;
}
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import wrapLocalData from './wrapLocalData';
import wrapAjax from './wrapAjax';
let UserName=(props) => {
console.log('UserName ',props);
return <input type="text" value={props.data} />
}
let Password=(props) => {
return <input type="text" value={props.data}/>
}
let NewUserName=wrapAjax(UserName,'username');
let NewPassword=wrapAjax(Password,'password');
class Form extends Component{
render() {
return (
<form>
<NewUserName />
<NewPassword/>
</form>
)
}
}
ReactDOM.render(<Form/>,document.querySelector('#root'));
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
import wrapLocalData from './wrapLocalData';
import wrapAjax from './wrapAjax';
let UserName=(props) => {
console.log('UserName ',props);
return <input type="text" value={props.data} />
}
let Password=(props) => {
return <input type="text" value={props.data}/>
}
UserName=wrapAjax(UserName);
UserName=wrapLocalData(UserName,'username');
class Form extends Component{
render() {
return (
<form>
<UserName />
</form>
)
}
}
ReactDOM.render(<Form/>,document.querySelector('#root'));