├── 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 ();
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 |
--------------------------------------------------------------------------------