自己react全家桶(一)

130 阅读4分钟

高阶组件

是一个函数,接收一个组件返回一个组件

function Child(props){
  return <div>child- {props.name}</div>
}

const foo = Cmp => prpps =>{
  return (
  	<Cmp {...props} />
  )
}

class Hoc extends React.Component {
  
  render(){
    const Foo = foo(Child)
    return (
    	<Foo name="aaa"/>
    )
  }
}

装饰器:高阶组件。不能是函数组件。

需要安装插件:@babel/plugin-proposal-decorators

用法:@组件

const foo = Cmp => prpps =>{
  return (
  	<Cmp {...props} />
  )
}

@foo
class Child extends React.Component{
	render(){
		retuen (
			<div>{this.props.name}</div>
		)
	}
}

class Hoc extends React.Component {
  
  render(){
 
    return (
    	<Child name="aaa"/>
    )
  }
}

antd表单适用

import React, { Component } from "react";
import { Form, Button, Input } from "antd";

const nameRules = {
  required: true,
  message: "please input ur name"
};
const passwordRules = {
  required: true,
  message: "please input ur password"
};

@Form.create()
class FormPage extends Component {
  submit = () => {
    const { getFieldsValue, getFieldValue, validateFields } = this.props.form;
    validateFields((err, values) => {
      if (err) {
        console.log("err", err);
      } else {
        console.log("success", values);
      }
    });
    //console.log("submit", getFieldsValue(), getFieldValue("name"));
  };
  render() {
    console.log("props", this.props);
    const { getFieldDecorator } = this.props.form;
    return (
      <div>
        <h3>FormPage</h3>
        <Form>
          <Form.Item label="用户名">
            {getFieldDecorator("name", { rules: [nameRules] })(<Input />)}
          </Form.Item>
          <Form.Item label="密码">
            {getFieldDecorator("password", { rules: [passwordRules] })(
              <Input type="password" />
            )}
          </Form.Item>
        </Form>
        <Button onClick={this.submit}>submit</Button>
      </div>
    );
  }
}

export default FormPage;

表单组件设计思路

1.表单组件要求实现数据收集,校验,提交等,可通过高阶组件扩展

2.高阶组件给表单组件传递一个input组件包装函数接管其输入事件并统一管理表单数据

3.高阶组件给表单组件传递一个校验函数使其具备数据校验功能。

自定义基础表单组件

// MyForm.js

import React, { Component } from "react";
import FormCreate from "../components/FormCreate";

const nameRules = {
  required: true,
  message: "please input your name"
};
const passwordRules = {
  required: true,
  message: "please input your password"
};

@FormCreate
class MyFormPage extends Component {
  submit = () => {
    const { getFieldsValue, getFieldValue, validateFields } = this.props;
    validateFields((err, values) => {
      if (err) {
        console.log("err", err);
      } else {
        console.log("success", values);
      }
    });
    // console.log("submit", getFieldsValue(), getFieldValue("name"));
  };
  render() {
    console.log("props", this.props);
    const { getFieldDecorator } = this.props;
    return (
      <div>
        <h3>MyFormPage</h3>
        {getFieldDecorator("name", { rules: [nameRules] })(<input />)}
        {getFieldDecorator("password", { rules: [passwordRules] })(
          <input type="password" />
        )}
        <button onClick={this.submit}>submit</button>
      </div>
    );
  }
}

export default MyFormPage;


// FormCreate.js
import React, { useState } from "react";

const FormCreate = Cmp => props => {
  const [state, setState] = useState({});
  const options = {};
  const setChange = event => {
    setState({
      ...state,
      [event.target.name]: event.target.value
    });
  };
  const getFieldDecorator = (field, option) => InputCmp => {
    options[field] = option;
    return React.cloneElement(InputCmp, {
      name: field,
      value: state[field] || "",
      onChange: setChange
    });
  };
  const getFieldsValue = () => {
    return { ...state };
  };
  const getFieldValue = field => {
    return state[field];
  };
  const validateFields = callback => {
    const err = [];
    for (let key in options) {
      if (state[key] === undefined) {
        err.push({
          [key]: "error!"
        });
      }
    }
    if (err.length) {
      callback(err, { ...state });
    } else {
      callback(undefined, { ...state });
    }
  };
  return (
    <Cmp
      {...props}
      getFieldDecorator={getFieldDecorator}
      getFieldsValue={getFieldsValue}
      getFieldValue={getFieldValue}
      validateFields={validateFields}
    />
  );
};
export default FormCreate;

reducer

reducer 就是一个纯函数,接收旧的 state action,返回新的state

(previousState, action) => newState

思考:有如下函数, 聚合成一个函数,并把第一个函数的返回值传递给下一个函数,如何处理。

function f1(arg){return arg}
function f2(arg){return arg}
function f3(arg){return arg}

方法:

function compose(...funcs) {
  const len = funcs.length;
  if (len === 0) {
    return arg => arg;
  }
  if (len === 1) {
    return funcs[0];
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

redux

1.需要一个store来存储数据

2.store里的reducer初始化state并定义state修改规则

3.通过dispatch一个action来提交对数据的修改

4.action提交到reducer函数里,根据传入的action的type,返回新的state

下面以累加器为例:

1.创建store,src/store/ReduxStore.js

import { createStore } from 'redux';

function counterReducer(state = 0, action) {
  switch (action.type) {
    case "add":
      return state + 1;
    case "minus":
      return state - 1;
    default:
      return state;
  }
}

const store = createStore(counterReducer) // createStore 第一个参数接收reducer

export default store;

2.创建ReduxPage

import React, { Component } from "react";
import store from "../store/";

export default class ReduxPage extends Component {
  componentDidMount() {
    // subscribe监听
    store.subscribe(() => {
      this.forceUpdate();
    });
  }
  minus = () => {
    store.dispatch({ type: "minus" });
  };
  add = () => {
    store.dispatch({ type: "add" });
  };

  render() {
    console.log("store", store);
    return (
      <div>
        <h3>ReduxPage</h3>
        <p>{store.getState()}</p>
        <button onClick={this.add}>add</button>
        <button onClick={this.minus}>minus</button>
      </div>
    );
  }
}

3.通过上面即可实现了累加器

redux使用注意点:

  1. createStore 创建store
  2. reducer 初始化、修改状态函数
  3. getState 获取状态值
  4. dispatch 提交更新
  5. subscribe 变更订阅

自己实现一个redux

核心实现:

  1. 存储状态state
  2. 获取状态getState
  3. 更新状态dispatch
  4. 变更订阅subscribe

实现:

export function createStore(reducer, enhancer) {
  // enhancer:中间件
  if (enhancer) {
    return enhancer(createStore)(reducer);
  }
  let currentState = undefined; // 初始状态
  const listeners = []; // 接收多个监听函数
  
  // 返回当前state即可
  function getState() {
    return currentState;
  }
  
  function dispatch(action) {
    currentState = reducer(currentState, action); // 返回新的状态
    listeners.map(listener => listener()); // 执行回调函数
  }
  
  // subscribe:接收一个回调函数,可能会监听多个。
  function subscribe(listener) {
    listeners.push(listener);
  }
  dispatch({ type: "curry_fe" }); // 初始化
  return {
    getState,
    dispatch,
    subscribe
  };
}

redux实现异步

redux默认只支持同步,如果需要支持异步,比如ajax。需要中间件支持。

中间件:一个函数,对store.dispatch升级。在发出action和执行reducer中间,添加其他功能。

以redux-thunk和redux-logger为例:

npm i redux-thunk redux-logger -S

使用中间件:

import { createStore, applyMiddleware } from "redux";
import logger from 'redux-logger';
import thunk from 'redux-thunk';

const store = createStore(reducer,applyMiddleware(thunk,logger))

在异步操作的时候使用:

const asyncAdd = () => {
  store.dispatch(() => {
    setTimeout(() => {
      // 异步结束后,执行dispatch
      dispatch({
        type:'asyncAdd'
      })
    },1000)
  })
}

<button onClick={this.asyncAdd}>异步增加</button>

自己实现中间件

核心:实现函数序列执行

实现:

export function applyMiddleware(...middlewares) {
  return createStore => (...args) => {
    const store = createStore(...args);
    let dispatch = store.dispatch;
    const midAPi = {
      getState: store.getState,
      dispatch
    };
    const chain = middlewares.map(mw => mw(midAPi));
    dispatch = compose(...chain)(dispatch);
    return { ...store, dispatch };
  };
}

function compose(...funcs) {
  const len = funcs.length;
  if (len === 0) {
    return arg => arg;
  }
  if (len === 1) {
    return funcs[0];
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

实现logger中间件

function logger() {
  return dispatch => action => {
    console.log(action.type + "执行了!");
    return dispatch(action);
  };
}

实现thunk中间件

function thunk() {
  return dispatch => action => {
    if (typeof action === "function") {
      return action(dispatch);
    }
    return dispatch(action);
  };
}