├── config.js ├── .gitignore ├── .babelrc ├── README.md ├── .eslintrc.js ├── index.html ├── src ├── components │ ├── container │ │ ├── usual.jsx │ │ ├── hoc-add-style.jsx │ │ ├── container-add-style.jsx │ │ ├── hoc-add-func.jsx │ │ ├── container-add-func.jsx │ │ └── hoc-usual.jsx │ ├── inheritance │ │ ├── ii-hoc.jsx │ │ ├── hijack-render.jsx │ │ └── usual.jsx │ ├── simple │ │ ├── simple-hoc.jsx │ │ ├── props-proxy-hoc.jsx │ │ ├── ref-hoc.jsx │ │ └── usual.jsx │ └── form │ │ ├── login.jsx │ │ └── form-create.jsx ├── index.less ├── utils.js ├── routes.jsx └── index.jsx ├── index.js ├── package.json └── webpack.config.js /config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | port: 3333, 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode/ 3 | .idea 4 | public 5 | .DS_Store 6 | 7 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | presets: ["react", "env", "stage-0"], 3 | plugins: ["transform-decorators-legacy"], 4 | } 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### react 高阶组件 2 | 3 | 高阶组件的一些用法 case 4 | 5 | - [原文](https://github.com/sunyongjian/blog/issues/25) -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb", 3 | "plugins": [ 4 | "react", 5 | "jsx-a11y", 6 | "import" 7 | ] 8 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | quick-start 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /src/components/container/usual.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | class Usual extends Component { 4 | 5 | render() { 6 | console.log(this.props, 'props'); 7 | return
8 | Usual 9 |
10 | } 11 | }; 12 | export default Usual; -------------------------------------------------------------------------------- /src/components/inheritance/ii-hoc.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | const iiHoc = WrappedComponent => class extends WrappedComponent { 5 | render() { 6 | console.log(this.state, 'state'); 7 | return super.render(); 8 | } 9 | }; 10 | 11 | export default iiHoc; -------------------------------------------------------------------------------- /src/index.less: -------------------------------------------------------------------------------- 1 | #root { 2 | text-align: center; 3 | } 4 | img { 5 | width: 200px; 6 | height: 200px; 7 | } 8 | .menu { 9 | margin: 30px auto; 10 | width: 50%; 11 | display: flex; 12 | justify-content: space-between; 13 | a { 14 | color: #108ee9; 15 | text-decoration: none; 16 | } 17 | } -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const compose = (...funcs) => (component) => { 2 | if (funcs.lenght === 0) { 3 | return component; 4 | } 5 | const last = funcs[funcs.length - 1]; 6 | return funcs.reduceRight((res, cur) => cur(res), last(component)); 7 | }; 8 | 9 | export { 10 | compose, 11 | }; 12 | -------------------------------------------------------------------------------- /src/components/simple/simple-hoc.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const simpleHoc = WrappedComponent => { 4 | console.log('simpleHoc'); 5 | return class extends Component { 6 | render() { 7 | return 8 | } 9 | } 10 | } 11 | export default simpleHoc; -------------------------------------------------------------------------------- /src/components/container/hoc-add-style.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const addStyle = WrappedComponent => class extends Component { 4 | 5 | render() { 6 | return (
7 | 8 |
); 9 | } 10 | }; 11 | 12 | export default addStyle; 13 | -------------------------------------------------------------------------------- /src/components/container/container-add-style.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import Usual from './usual'; 3 | 4 | class StyleContainer extends Component { 5 | 6 | render() { 7 | return (
8 |
container
9 | 10 |
); 11 | } 12 | } 13 | 14 | export default StyleContainer; 15 | -------------------------------------------------------------------------------- /src/components/simple/props-proxy-hoc.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const propsProxyHoc = WrappedComponent => class extends Component { 4 | 5 | handleClick() { 6 | console.log('click'); 7 | } 8 | 9 | render() { 10 | return (); 14 | } 15 | }; 16 | export default propsProxyHoc; 17 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const path = require('path'); 3 | const config = require('./config'); 4 | 5 | const app = express(); 6 | 7 | app.use(express.static(__dirname + '/public')); 8 | app.use(function(req, res, next) { 9 | res.sendFile(path.resolve(__dirname + '/public/index.html')); 10 | }); 11 | 12 | app.listen(config.port, function() { 13 | console.log('listen:', config.port); 14 | }); 15 | -------------------------------------------------------------------------------- /src/components/container/hoc-add-func.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const addFunc = WrappedComponent => class extends Component { 4 | handleClick() { 5 | console.log('click'); 6 | } 7 | render() { 8 | const props = { 9 | ...this.props, 10 | handleClick: this.handleClick, 11 | }; 12 | return ; 13 | } 14 | }; 15 | 16 | export default addFunc; 17 | -------------------------------------------------------------------------------- /src/components/simple/ref-hoc.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const refHoc = WrappedComponent => class extends Component { 4 | 5 | componentDidMount() { 6 | console.log(this.instanceComponent, 'instanceComponent'); 7 | } 8 | 9 | render() { 10 | return ( this.instanceComponent = instanceComponent} 13 | />); 14 | } 15 | }; 16 | export default refHoc; 17 | -------------------------------------------------------------------------------- /src/components/container/container-add-func.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import StyleContainer from './container-add-style'; 3 | 4 | class FuncContainer extends Component { 5 | handleClick() { 6 | console.log('click'); 7 | } 8 | 9 | render() { 10 | const props = { 11 | ...this.props, 12 | handleClick: this.handleClick, 13 | }; 14 | return (); 15 | } 16 | } 17 | 18 | export default FuncContainer; 19 | -------------------------------------------------------------------------------- /src/components/inheritance/hijack-render.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const hijackRenderHoc = config => WrappedComponent => class extends WrappedComponent { 4 | render() { 5 | const { style = {} } = config; 6 | const elementsTree = super.render(); 7 | console.log(elementsTree, 'elementsTree'); 8 | if (config.type === 'add-style') { 9 | return
10 | {elementsTree} 11 |
; 12 | } 13 | return elementsTree; 14 | } 15 | }; 16 | 17 | export default hijackRenderHoc; -------------------------------------------------------------------------------- /src/components/container/hoc-usual.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import addStyle from './hoc-add-style'; 3 | import addFunc from './hoc-add-func'; 4 | import Usual from './usual'; 5 | import { compose } from '../../utils'; 6 | 7 | // const WrappenComponent = addStyle(addFunc(Usual)); 8 | const WrappenComponent = compose(addFunc, addStyle)(Usual); 9 | 10 | class WrappedUsual extends Component { 11 | 12 | render() { 13 | console.log(this.props, 'props'); 14 | return (
15 | 16 |
); 17 | } 18 | } 19 | 20 | export default WrappedUsual; 21 | -------------------------------------------------------------------------------- /src/components/inheritance/usual.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import hijackRenderHoc from './hijack-render'; 3 | import iiHoc from './ii-hoc'; 4 | 5 | @hijackRenderHoc({type: 'add-style', style: { color: 'red'}}) 6 | @iiHoc 7 | export default class Usual extends Component { 8 | static log() { 9 | console.log('log') 10 | } 11 | constructor() { 12 | super(); 13 | this.state = { 14 | usual: 'usual', 15 | } 16 | } 17 | 18 | componentDidMount() { 19 | console.log('didMount') 20 | } 21 | 22 | render() { 23 | return ( 24 |
25 | Usual 26 |
27 | ) 28 | } 29 | } -------------------------------------------------------------------------------- /src/components/simple/usual.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import simpleHoc from './simple-hoc'; 3 | import propsProxyHoc from './props-proxy-hoc'; 4 | import refHoc from './ref-hoc'; 5 | import { compose } from '../../utils'; 6 | 7 | // 注意我这里写的顺序。 8 | @simpleHoc 9 | @propsProxyHoc 10 | @refHoc 11 | export default class Usual extends Component { 12 | 13 | constructor() { 14 | super(); 15 | this.state = { 16 | usual: 'usual', 17 | } 18 | } 19 | 20 | render() { 21 | console.log(this.props, this.state, 'props'); 22 | return ( 23 |
24 | Usual 25 |
26 | ) 27 | } 28 | } 29 | 30 | // export default compose(simpleHoc, propsProxyHoc, refHoc)(Usual); -------------------------------------------------------------------------------- /src/components/form/login.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import formCreate from './form-create'; 3 | 4 | @formCreate 5 | export default class Login extends Component { 6 | render() { 7 | return ( 8 |
9 |
10 | 13 | 14 |
15 |
16 | 19 | 20 |
21 |
提交
22 |
other content
23 |
24 | ) 25 | } 26 | } -------------------------------------------------------------------------------- /src/routes.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | import Usual from './components/simple/usual'; 4 | import Usual1 from './components/inheritance/usual'; 5 | import Login from './components/form/login'; 6 | import FuncContainer from './components/container/container-add-func'; 7 | import WrappedUsual from './components/container/hoc-usual'; 8 | 9 | const Routes = () => ( 10 |
11 |
Welcome to
} /> 12 | 13 | 14 | 15 | 16 | 17 |
18 | ); 19 | export default Routes; 20 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from 'react-dom'; 3 | import { BrowserRouter as Router, Link } from 'react-router-dom'; 4 | import Routes from './routes'; 5 | import './index.less'; 6 | 7 | class App extends React.Component { 8 | render() { 9 | return ( 10 |
11 |

welcome to react-quick-start

12 | 13 |
14 |
15 | 属性代理 16 | 抽离state 17 | 反向继承 18 | Container 19 | 跟Container对比 20 |
21 | 22 |
23 |
24 |
25 | ); 26 | } 27 | } 28 | 29 | render(, document.getElementById('root')); 30 | 31 | -------------------------------------------------------------------------------- /src/components/form/form-create.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | 3 | const formCreate = WrappedComponent => class extends Component { 4 | 5 | constructor() { 6 | super(); 7 | this.state = { 8 | fields: {}, 9 | } 10 | } 11 | 12 | onChange = key => e => { 13 | this.setState({ 14 | fields: { 15 | ...this.state.fields, 16 | [key]: e.target.value, 17 | } 18 | }) 19 | } 20 | 21 | handleSubmit = () => { 22 | console.log(this.state.fields); 23 | } 24 | 25 | getField = fieldName => { 26 | return { 27 | onChange: this.onChange(fieldName), 28 | } 29 | } 30 | 31 | render() { 32 | const props = { 33 | ...this.props, 34 | handleSubmit: this.handleSubmit, 35 | getField: this.getField, 36 | } 37 | 38 | return (); 41 | } 42 | }; 43 | export default formCreate; 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-quick-start", 3 | "version": "1.0.0", 4 | "description": "quickly start react demo", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --hot --inline", 8 | "build": "rm -rf public && webpack", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "keywords": [ 12 | "react" 13 | ], 14 | "author": "sunyongjian", 15 | "license": "ISC", 16 | "devDependencies": { 17 | "babel-core": "^6.25.0", 18 | "babel-loader": "^7.0.0", 19 | "babel-plugin-transform-decorators": "^6.24.1", 20 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 21 | "babel-preset-env": "^1.6.1", 22 | "babel-preset-react": "^6.24.1", 23 | "babel-preset-stage-0": "^6.24.1", 24 | "css-loader": "^0.28.4", 25 | "express": "^4.15.3", 26 | "file-loader": "^0.11.2", 27 | "html-webpack-plugin": "^2.28.0", 28 | "less": "^2.7.2", 29 | "less-loader": "^4.0.4", 30 | "open-browser-webpack-plugin": "0.0.5", 31 | "style-loader": "^0.18.2", 32 | "webpack": "^3.1.0", 33 | "webpack-dev-server": "^2.5.1" 34 | }, 35 | "dependencies": { 36 | "react": "^15.6.1", 37 | "react-dom": "^15.6.1", 38 | "react-router-dom": "^4.1.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const OpenBrowserPlugin = require('open-browser-webpack-plugin'); 5 | const config = require('./config'); 6 | 7 | module.exports = { 8 | entry: { 9 | app: './src/index.jsx', 10 | vendor: ['react', 'react-dom'], 11 | }, 12 | output: { 13 | filename: '[name].[hash].js', 14 | path: path.resolve(__dirname, 'public'), 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.(jsx|js)$/, 20 | loader: 'babel-loader', 21 | }, 22 | { 23 | test: /\.(less|css)$/, 24 | use: ['style-loader', 'css-loader', 'less-loader'], 25 | }, 26 | { 27 | test: /\.(png|jpg|svg)$/, 28 | loader: 'file-loader', 29 | }, 30 | ], 31 | }, 32 | plugins: [ 33 | new HtmlWebpackPlugin({ 34 | title: 'quick-start', 35 | template: 'index.html', 36 | }), 37 | new webpack.optimize.CommonsChunkPlugin({ 38 | name: 'vendor', 39 | }), 40 | new OpenBrowserPlugin({ 41 | url: 'http://localhost:3333/', 42 | }), 43 | ], 44 | devServer: { 45 | contentBase: path.resolve(__dirname, 'public'), 46 | port: config.port, 47 | historyApiFallback: true, 48 | }, 49 | resolve: { 50 | extensions: ['*', '.js', '.jsx',], 51 | } 52 | }; 53 | --------------------------------------------------------------------------------