react 总结和技巧

目录

  • 1 netlify
  • 2 Github Pages
  • 3 无状态组件(函数式组件)
  • 4 优化 PureComponent
  • 5 Fragment
  • 6 context
  • 7 高阶组价
    7.1 使用高阶组件重构
    7.2 实际项目使用高阶优化1
    7.3 参考高阶代码
  • 8 react-powerplug
  • 9 高阶组件,处理多个平级组件
  • 10 propTypes 、defaultProps
  • 11 Render Props
  • 12 Error Boundary
  • 13 bind this
  • 14 16.3 Context
  • 15 使用16.3 Context 改写 redux
  • 16 可变数据
  • 17 this.props.children
  • 18 随记

1 netlify

netlify 是一个提供托管静态网站的服务。

官网:https://app.netlify.com/

react 总结和技巧_第1张图片
部署
react 总结和技巧_第2张图片
更改名称

2 Github Pages

部署到 Github Pages(Github Pages是面向用户、组织和项目开放的公共静态页面搭建托管服务)

步骤:

  • 安装 gh-pages
    https://github.com/tschaub/gh-pages
npm install gh-pages --save-dev 

  • 在 package.json 增加 homepage
"homepage": "https://hongzelin.github.io/GitHubPageTest",

说明:https://用户名.github.io/仓库名


  • 在 package.json 在 scripts 增加 deploy
"scripts": {
 "predeploy": "yarn run build",  //  这个是在 执行 deploy命令之前执行,打出静态包,放在 gh-pages 新分支上 
  "deploy": "gh-pages -d build"  // 参考 gh-pages 官网
}

  • 运行
yarn run deploy

说明:这时候在GitHub项目仓库会打出来新的分支 gh-pages,
接下来,直接访问 homepage 配置的网址即可。

3 无状态组件(函数式组件)

参考:https://www.jianshu.com/p/63569386befc

理解基础概念:

import React from 'react';

const Header = (props) => {
  return (
    

Header

); }; export default Header; 说明:无状态组件,也叫函数式组件;定义一个箭头函数,传入 props 参数。 只有 return,没有 render 方法; 注意:如果需要操作状态,这时候需要配合高阶组价一起使用。 参考:https://zhuanlan.zhihu.com/p/24776678?group_id=802649040843051008

4 优化 PureComponent

组件继承 PureComponent ,避免组件重复渲染

import React, { Component, PureComponent } from 'react';
import logo from './logo.svg';
import './App.css';

// 无状态组件一般写法,在 props 没有改变的时候,也会重复渲染
// const Temp = (props) => {
//   console.log('render Temp');
//   return (
//     
{ props.val }
// ); // } // 使用 es6 的写法,继承 PureComponent ,解决在 props 没有改变的时候,重复渲染问题 class Temp extends PureComponent { render() { console.log('render Temp'); return (
{ this.props.val }
); } } class App extends Component { state = { val: 1 } componentDidMount() { setInterval(() => { // 演示,值没有改变的时候,无状态组件是否还是会重新渲染。 this.setState({ val: 1 }) }, 2000) } // shouldComponentUpdate(nextProps, nextState) { // console.log('nextState', nextState); // console.log('current state', this.state); // return ( // this.state.val === nextState.val ? false : true // ) // } render() { console.log('render App'); return (
logo

Welcome to React

To get started, edit src/App.js and save to reload.

); } } export default App;

5 Fragment

Fragment 是为了解决需要根节点报错问题;

使用 Fragment,在 DOM 生成并不会引入多余的标签;

另一个方法也可以使用:<> ... ,包裹解决;

import React, { Component, Fragment } from 'react';
import logo from './logo.svg';
import './App.css';

const Temp = (props) => {
  return (
    
      
  • list 1
  • list 1
  • ) } class App extends Component { render() { return ( {/* ul的子节点一定是li */}
    ); } } export default App;

    6 context

    参考:https://segmentfault.com/a/1190000002878442

    通过 context 传递属性的方式可以大量减少,显式通过 props 逐层传递属性的方式。这样可以减少组件之间的直接依赖关系。

    import React, { Component } from 'react';
    import './App.css';
    import PropTypes from 'prop-types';
    
    const Topic = (props) => {
      return (
        
    ) } const Comment = (props, context) => { return (
    { context.color }
    ) } Comment.contextTypes = { // 子组件需要指定 contextTypes color: PropTypes.string } class App extends Component { getChildContext() { return { color: "red" }; } render() { return (
    ); } } App.childContextTypes = { // 父组件需要制定childContextTypes color: PropTypes.string } export default App;

    7 高阶组价

    参考:
    https://www.jianshu.com/p/0aae7d4d9bc1
    https://zhuanlan.zhihu.com/p/24776678?group_id=802649040843051008

    简单理解,高阶组价就是传入一个组件参数,返回一个组件。

    import React, { Component } from 'react';
    import './App.css';
    
    const PropsLogger = (WrapperComponent) => {
      return class extends Component {
        render() {
          return 
        }
      }
    }
    
    // PropsLogger 高阶组件传入一个组件函数,作为参数。
    const Hello = PropsLogger((props) => {
      return (
        

    Hello { props.name }

    ) }) class App extends Component { render() { return (
    // 这里的参数,是传递高阶组价,Hello组件是等于高阶组件。
    ); } } export default App; 说明:简单的理解就是,高阶组价就是传入一个组件参数,返回一个组件。

    7.1 使用高阶组件重构

    重构前

    • src/components/User.js
    import React, { Component } from 'react';
    
    class User extends Component {
      constructor() {
        super();
        this.state = {
          loading: true,
          user: null
        };
      }
    
      componentDidMount() {
        fetch('https://randomuser.me/api/')
          .then(res => res.json())
          .then(user => {
            this.setState({
              loading: false,
              user: user
            });
          })
      }
    
      render() {
        if(this.state.loading) {
          return (
            
    loading
    ) } else { return (

    {this.state.user.results[0].email}

    ) } } } export default User;
    • src/components/Joke.js
    import React, { Component } from 'react';
    
    class Joke extends Component {
      constructor() {
        super();
        this.state = {
          loading: true,
          jokes: null
        };
      }
    
      componentDidMount() {
        fetch('http://api.icndb.com/jokes/random/3')
          .then(res => res.json())
          .then(jokes => {
            this.setState({
              loading: false,
              jokes: jokes
            });
          })
      }
    
      render() {
        if(this.state.loading) {
          return (
            
    loading
    ) } else { return (
    { this.state.jokes.value.map(joke => (

    { joke.joke }

    )) }
    ) } } } export default Joke;

    重构后:使用高阶组件

    • src/hoc/withFetch.js
    import React, { Component } from 'react';
    
    const withFetch = (url) => (View) => {
      return class extends Component {
        constructor() {
          super();
          this.state = {
            loading: true,
            data: null
          };
        }
    
        componentDidMount() {
          fetch(url)
            .then(res => res.json())
            .then(data => {
              this.setState({
                loading: false,
                data: data
              });
            })
        }
    
        render() {
          if(this.state.loading) {
            return (
              
    loading
    ) } else { return } } } } export default withFetch;
    • src/components/Joke.js(调用高阶组件)
    import React from 'react';
    import withFetch from '../hoc/withFetch';
    
    const Joke = withFetch('http://api.icndb.com/jokes/random/3')(props => {
      return (
        
    { props.data.value.map(joke => (

    { joke.joke }

    )) }
    ) }) export default Joke;
    • src/components/User.js
    import React from 'react';
    import withFetch from '../hoc/withFetch';
    
    const User = withFetch('https://randomuser.me/api/')(props => {
      return (
        

    {props.data.results[0].email}

    ) }) export default User;

    7.2 实际项目使用高阶优化1

    页面引用不同的组件,关键点,传入给各个组件 props 属性!!!
    
    import React from 'react'
    import OperatorUI from './OperatorUI'
    import StartUI from './StartUI'
    import EndUI from './EndUI'
    import StrategyUI from './StrategyUI'
    
    
      getRenderSortFlow(info, styleObj) {
        let { type } = info;
        type = type.indexOf('strategy') > -1 ? 'strategy' : type;
    
        switch (type) {
          case 'start': // 开始节点
            return (
              
            );
          case 'end': // 结束节点
            return (
              
            );
          case 'algorithm': // 算子
            return (
              
            );
          case 'strategy': // 策略
            return (
              
            );
          default:
            return (
              
    {info.name}
    ); } }
    StartUI 组件:
    
    import React from 'react'
    import HocNodeUI from '../HocUI/HocNodeUI'
    
    const StartUI = HocNodeUI(({ info }) => (
      
    {info.name}
    )) export default StartUI;
    EndUI 组件:
    
    import React from 'react'
    import HocNodeUI from '../HocUI/HocNodeUI'
    
    const EndUI = HocNodeUI(({ info }) => (
      
    {info.name}
    )) export default EndUI;
    OperatorUI 组件:
    
    import React from 'react'
    import { Icon } from '@ali/wind'
    import HocNodeUI from '../HocUI/HocNodeUI'
    
    const del = (e, id, eventObj) => {
      e.stopPropagation();
      if (id) {
        eventObj.onRemove(id);
      }
    }
    
    const OperatorUI = HocNodeUI(({ info, eventObj }) => (
      
    {info.name} del(e, info.uuid, eventObj)} />
    )) export default OperatorUI;
    StrategyUI 组件:
    
    import React from 'react'
    import { Icon } from '@ali/wind'
    import HocNodeUI from '../HocUI/HocNodeUI'
    import './StrategyUI.less'
    
    const del = (e, id, eventObj) => {
      e.stopPropagation();
      if (id) {
        eventObj.onRemove(id);
      }
    }
    
    const StrategyUI = HocNodeUI(({ info, eventObj }) => (
      
    <⁄> {info.name} del(e, info.uuid, eventObj)} />
    )) export default StrategyUI;
    /*
     * @Author: lin.zehong
     * @Date: 2019-06-13 10:22:11
     * @Last Modified by: lin.zehong
     * @Last Modified time: 2019-06-13 13:58:50
     * @Desc: 高阶:开始、结束、算子、策略等UI高阶
     */
    
    import React, { Component } from 'react'
    import { connect } from 'dva'
    import PropTypes from 'prop-types'
    import { selectors } from '../../../models/tasksFlow'
    import './HocNodeUI.less'
    
    const HocNodeUI = (WrappedComponent) => {
      const mapStateToProps = state => ({
        isActiveAlgo: selectors.getIsActiveAlgo(state),
      })
    
      @connect(mapStateToProps, null)
      class HocUI extends Component {
        static propTypes = {
          eventObj: PropTypes.objectOf(PropTypes.any),
          info: PropTypes.objectOf(PropTypes.any),
          styleObj: PropTypes.objectOf(PropTypes.any),
          isActiveAlgo: PropTypes.string,
        }
    
        render() {
          const { info, styleObj, eventObj, isActiveAlgo } = this.props;
          return (
            
    { eventObj.onClick(e, info) }} onMouseUp={(e) => { eventObj.onMouseUp(e, info) }} >
    ) } } return HocUI; } export default HocNodeUI;
    react 总结和技巧_第3张图片
    思路流程图

    注意:
    1:除了使用高阶包裹的方式,组件使用高阶也可以使用注解的方式:

    import React from 'react'
    import PropTypes from 'prop-types'
    import HocNodeUI from '../HocUI/HocNodeUI'
    
    @HocNodeUI
    class EndUI extends React.Component {
      render() {
        const { info } = this.props;
        return (
          
    {info.name}
    ) } } EndUI.propTypes = { info: PropTypes.objectOf(PropTypes.any), } // const EndUI = HocNodeUI(({ info }) => ( //
    // // // // // {info.name} // //
    // )) export default EndUI;

    2:传递参数给高阶组件

    /*
     * @Author: lin.zehong
     * @Date: 2019-06-13 10:22:11
     * @Last Modified by: lin.zehong
     * @Last Modified time: 2019-06-13 15:15:09
     * @Desc: 高阶:开始、结束、算子、策略等UI高阶
     */
    
    import React, { Component } from 'react'
    import { connect } from 'dva'
    import PropTypes from 'prop-types'
    import { selectors } from '../../../models/tasksFlow'
    import './HocNodeUI.less'
    
    const HocNodeUI = (class_name) => (WrappedComponent) => {
      const mapStateToProps = state => ({
        isActiveAlgo: selectors.getIsActiveAlgo(state),
      })
    
      @connect(mapStateToProps, null)
      class HocUI extends Component {
        static propTypes = {
          eventObj: PropTypes.objectOf(PropTypes.any),
          info: PropTypes.objectOf(PropTypes.any),
          styleObj: PropTypes.objectOf(PropTypes.any),
          isActiveAlgo: PropTypes.string,
        }
    
        render() {
          const { info, styleObj, eventObj, isActiveAlgo } = this.props;
          return (
            
    { eventObj.onClick(e, info) }} onMouseUp={(e) => { eventObj.onMouseUp(e, info) }} >
    ) } } return HocUI; } export default HocNodeUI;
    方式一:使用注释,也就是高阶的参数:
    
    import React from 'react'
    import PropTypes from 'prop-types'
    import HocNodeUI from '../HocUI/HocNodeUI'
    
    @HocNodeUI('l-o-wrap')
    class EndUI extends React.Component {
      render() {
        const { info } = this.props;
        return (
          
    {info.name}
    ) } } EndUI.propTypes = { info: PropTypes.objectOf(PropTypes.any), } ·········································································· 方式二:使用高阶包裹方式: import React from 'react' import { Icon } from '@ali/wind' import HocNodeUI from '../HocUI/HocNodeUI' const del = (e, id, eventObj) => { e.stopPropagation(); if (id) { eventObj.onRemove(id); } } const OperatorUI = HocNodeUI('l-o-wrap')(({ info, eventObj }) => (
    {info.name} del(e, info.uuid, eventObj)} />
    )) export default OperatorUI; ·········································································· 方式三:如果不需要传递参数,也是需要括号调用: import React from 'react' import { Icon } from '@ali/wind' import HocNodeUI from '../HocUI/HocNodeUI' import './StrategyUI.less' const del = (e, id, eventObj) => { e.stopPropagation(); if (id) { eventObj.onRemove(id); } } const StrategyUI = HocNodeUI()(({ info, eventObj }) => (
    <⁄> {info.name} del(e, info.uuid, eventObj)} />
    )) export default StrategyUI;

    7.3 参考高阶代码

    import React, { Component } from 'react'
    import PropTypes from 'prop-types'
    import './HocUI.less'
    
    /**
     * 用于包装在绘图区域拖拽生成的节点,方便后期扩展功能或者UI
     * 页面上面渲染的每个节点都需要从此高阶组件过,
     * 那么就在这里对正、反渲染的逻辑进行判断,给节点做拦截,
     * 新增一些方法、属性、样式上去
     *
     * @param WrappedComponent
     * @returns {{new(): {render(): *}}}
     * @constructor
     */
    function HocNodeFlowMenu(className) {
      return function (WrappedComponent) {
        return class HocNodeFlowMenuUI extends Component {
          static propTypes = {
            info: PropTypes.objectOf(PropTypes.any),
            styleObj: PropTypes.objectOf(PropTypes.any),
            eventObj: PropTypes.objectOf(PropTypes.any),
          }
    
          constructor(props) {
            super(props);
            this.state = {
              visiable: false,
            }
            this.handleMouseMove = this.handleMouseMove.bind(this);
            this.handleMouseLeave = this.handleMouseLeave.bind(this);
          }
    
          handleMouseMove() {
            this.setState(() => ({
              visiable: true,
            }))
          }
    
          handleMouseLeave() {
            this.setState(() => ({
              visiable: false,
            }))
          }
    
          render() {
            const { info, styleObj, eventObj } = this.props;
            return (
              
    { eventObj.onClick(info) }} onMouseUp={(e) => { eventObj.onMouseUp(e, info) }} onContextMenu={(e) => { eventObj.onContextmenu(e) }} >
    ) } } } } export default HocNodeFlowMenu;

    8 react-powerplug

    react-powerplug 插件

    • 重构前
    import React, { Component } from 'react';
    import './App.css';
    
    class Counter extends Component {
      state = {
        counter: 0
      }
    
      increment = () => {
        this.setState(prevState => ({ counter: prevState.counter + 1 }));
      }
    
      decrement = () => {
        this.setState(prevState => ({ counter: prevState.counter - 1 }));
      }
    
      render() {
        return (
          
    Counter: { this.state.counter }
    ); } } export default Counter;
    • 重构后
    import React, { Component } from 'react';
    import './App.css';
    import { State } from 'react-powerplug'
    
    class Counter extends Component {
      render() {
        return (
          
            {({ state, setState }) => (
              
    Counter: { state.counter }
    )}
    ); } } export default Counter;

    9 高阶组件,处理多个平级组件

    import React, { Component } from 'react';
    import './App.css';
    
    const Wrapper = ({ children }) => children;
    
    const Hello = ({ name }) => {
      return (
        
          

    React 16 rocks

    Hello, { name }!

    ) } class App extends Component { render() { return (
    ); } } export default App; 说明: const Wrapper = ({ children }) => children; 这个相关于 const Wrapper = ( props ) => props.children;,这样更容易理解些吧?

    10 propTypes 、defaultProps

    import React, { Component } from 'react';
    import './App.css';
    import PropTypes from 'prop-types';
    
    class Hello extends Component {
      // static defaultProps = {
      //   name: "rails365"
      // }
    
      render() {
        return (
          

    Hello, { this.props.money }, { this.props.name }

      { this.props.movies.map(movie =>
    • { movie.title }
    • ) }
    ) } } Hello.propTypes = { money: PropTypes.number, onChange: PropTypes.func.isRequired, name: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]), // movies是个数组,PropTypes.shape表示数组里面的每个元素是个对象,再对里面的对象进行细致限制。 movies: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.number, title: PropTypes.string, visit_count: PropTypes.number })) } // Hello.defaultProps = { // name: "rails365" // } class App extends Component { onChange() { } state = { movies: [ { id: 1, title: 'title 1', visit_count: 1 }, { id: 2, title: 'title 2', visit_count: 2 } ] } render() { return (
    ); } } export default App;

    11 Render Props

    参考:
    https://zhuanlan.zhihu.com/p/31267131
    https://reactjs.org/docs/render-props.html

    • 重写前
    import React from 'react';
    
    const withMouse = (Component) => {
      return class extends React.Component {
        state = { x: 0, y: 0 }
    
        handleMouseMove = (event) => {
          this.setState({
            x: event.clientX,
            y: event.clientY
          })
        }
    
        render() {
          return (
            
    ) } } } const App = (props) => { const { x, y } = props.mouse return (

    The mouse position is ({ x }, { y })

    ) } const AppWithMouse = withMouse(App) export default AppWithMouse;
    • 重写后
    import React from 'react';
    import PropTypes from 'prop-types';
    
    class Mouse extends React.Component {
      static propTypes = {
        render: PropTypes.func.isRequired
      }
    
      state = { x: 0, y: 0 }
    
      handleMouseMove = (event) => {
        this.setState({
          x: event.clientX,
          y: event.clientY
        })
      }
    
      render() {
        return (
          
    { this.props.render(this.state) }
    ) } } const Position = ({ x, y }) => { return (

    The mouse position is ({ x }, { y })

    ) } const Position1 = ({ x, y }) => { return (

    The mouse position is ({ x }, { y })

    ) } const App = () => { return (
    } /> } /> (

    The mouse position is ({ x }, { y })

    )} />
    ) } export default App;

    12 Error Boundary

    针对的是其子节点,在生产环境,如果该子节点发生错误,则只有

    • src/App.js
    import React, { Component } from 'react';
    import './App.css';
    import Broken from './Broken';
    import ErrorBoundary from './ErrorBoundary';
    
    class App extends Component {
      state = {
        counter: 0
      }
    
      increment = () => {
        this.setState(prevState => ({ counter: prevState.counter + 1 }));
      }
    
      decrement = () => {
        this.setState(prevState => ({ counter: prevState.counter - 1 }));
      }
    
      render() {
        return (
          

    Hello rails365

    Error: { error.toString() }

    }>
    Counter: { this.state.counter }
    ); } } export default App;
    • src/ErrorBoundary.js
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    
    class ErrorBoundary extends Component {
      static propTypes = {
        children: PropTypes.oneOfType([
          PropTypes.node,
          PropTypes.arrayOf(PropTypes.node)
        ]).isRequired
      }
    
      state = {
        hasError: false,
        error: null,
        errorInfo: null
      }
    
      componentDidCatch(error, errorInfo) {
        this.setState({
          hasError: true,
          error: error,
          errorInfo: errorInfo
        })
      }
    
      render() {
        if (this.state.hasError) {
          return 
    { this.props.render(this.state.error, this.state.errorInfo) }
    } return this.props.children; } } export default ErrorBoundary;

    13 bind this

    参考:https://www.jianshu.com/p/018665bc4ce2

    基本有4/5种写法

    • 最好的写法是使用es6,箭头函数的写法
    import React, { Component } from 'react';
    import logo from './logo.svg';
    import './App.css';
    
    class App extends Component {
      constructor(props) {
        super(props)
        this.state = {
          name: ''
        }
      }
    
      handleChange = e => {
        this.setState({
          name: e.target.value
        })
      }
    
      render() {
        return (
          
    logo

    Welcome to React

    { this.state.name }

    ); } } export default App;

    14 16.3 Context

    import React, { Component } from 'react';
    import './App.css';
    
    // 第一步,创建 context
    const myContext = React.createContext()
    
    // 第二步,创建 Provider Component
    
    class MyProvider extends Component {
      state = {
        name: "rails365",
        age: 27
      }
    
      render() {
        return (
          
            { this.props.children }
          
        )
      }
    }
    
    const Family = (props) => {
      return (
        

    Family

    ) } class Person extends Component { render() { return (

    Person

    { ({ state }) =>

    My age is { state.age }

    }
    ); } } class App extends Component { render() { return (

    Hello App

    ); } } export default App;

    16.3 Context 进一步理解

    • src/App.js
    import React, { Component } from 'react';
    import './App.css';
    import { CDNFlagIcon } from "react-flag-kit";
    
    const ThemeContext = React.createContext();
    
    const localeMap = {
      "en-US": { locale: "en-US", flag: "US", content: "Hello, World!" },
      "fr-FR": { locale: "fr-FR", flag: "FR", content: "Bonjour le monde!" },
      "es-ES": { locale: "es-ES", flag: "ES", content: "¡Hola Mundo!" }
    };
    
    class LocaleSwitcher extends Component {
      state = localeMap["en-US"]
      render() {
        return (
           this.setState(localeMap[e.target.value])
            }}
          >
            { this.props.children }
          
        );
      }
    }
    
    const LocaleSelect = () => {
      return (
        
          {context => (
            
          )}
        
      )
    }
    
    const LocaleFlag = (props) => {
      return (
        
          { context =>  }
        
      )
    }
    
    const LocaleContent = (props) => {
      return (
        
          { context => 

    { context.state.content }

    }
    ) } class App extends Component { render() { return ( ); } } export default App;

    15 使用16.3 Context 改写 redux

    主要思想,哪里提供数据,哪里消费数据

    • src/contexts/ReminderContext.js
    import React, { Component } from 'react';
    import { bake_cookie, read_cookie } from 'sfcookies';
    
    export const ReminderContext = React.createContext();
    
    export class ReminderProvider extends Component {
      state = {
        reminders: read_cookie("reminders") || []
      }
    
      addReminder = (text, dueDate) => {
        let reminders = [];
        reminders = [
          ...this.state.reminders,
          { id: Math.random(), text, dueDate }
        ];
        this.setState({
          reminders: reminders
        });
        bake_cookie("reminders", reminders);
      }
    
      deleteReminder = (id) => {
        let reminders = [];
        reminders = this.state.reminders.filter(reminder => reminder.id !== id);
        this.setState({
          reminders: reminders
        });
        bake_cookie("reminders", reminders);
      }
    
      clearReminders = () => {
        this.setState({
          reminders: []
        });
        bake_cookie("reminders", []);
      }
    
      render() {
        return (
          
            { this.props.children }
          
        );
      }
    }
    
    
    • src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './components/App';
    import registerServiceWorker from './registerServiceWorker';
    
    import { ReminderContext, ReminderProvider } from './contexts/ReminderContext';
    
    ReactDOM.render(
      
        
          { ({ reminders, clearReminders, addReminder, deleteReminder }) =>
            ()
          }
        
      ,
      document.getElementById('root')
    );
    registerServiceWorker();
    
    
    
    • src/components/App.js
    import React, { Component } from 'react';
    import PropTypes from 'prop-types';
    import moment from 'moment';
    
    class App extends Component {
      constructor(props) {
        super(props)
        this.state = {
          text: '',
          dueDate: ''
        };
      }
    
      addReminder() {
        this.props.addReminder(this.state.text, this.state.dueDate);
      }
    
      deleteReminder(id) {
        this.props.deleteReminder(id);
      }
    
      clearReminders() {
        this.props.clearReminders();
      }
    
      renderReminders() {
        const { reminders } = this.props;
        return (
          
      { reminders.map(reminder => { return (
    • { reminder.text }
      { moment(new Date(reminder.dueDate)).fromNow() }
      this.deleteReminder(reminder.id) } > ✕
    • ); }) }
    ); } render() { return (
    Reminder Pro
    this.setState({text: event.target.value}) } /> this.setState({dueDate: event.target.value}) } />
    { this.renderReminders() }
    this.clearReminders() } > Clear Reminders
    ); } } App.propTypes = { reminders: PropTypes.array.isRequired, addReminder: PropTypes.func.isRequired, deleteReminder: PropTypes.func.isRequired, clearReminders: PropTypes.func.isRequired } export default App;

    16 可变数据

    • 学习资源

    https://medium.com/@fknussel/arrays-objects-and-mutations-6b23348b54aa

    https://github.com/hfpp2012/redux-reminder-pro/blob/master/src/reducers/index.js

    https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

    https://lodash.com/docs/

    17 this.props.children

    • 学习资源

    https://reactjs.org/docs/composition-vs-inheritance.html

    https://mxstbr.blog/2017/02/react-children-deepdive/#enforcing-a-single-child

    https://reactjs.org/docs/jsx-in-depth.html

    https://reactjs.org/docs/react-api.html#createelement

    https://stackoverflow.com/questions/29464577/why-doesnt-this-props-children-map-work

    https://stackoverflow.com/questions/35616029/react-createelement-vs-cloneelement

    https://segmentfault.com/a/1190000008587988

    https://learn.co/lessons/react-this-props-children

    18 随记

    AuthorizedRoute:
    在应用程序中限制未登录的用户访问某些路由是非常常见的,还有对于授权和未授权的用户 UI 也可能大不一样,为了解决这样的需求,我们可以考虑为应用程序设置一个主入口
    https://www.ctolib.com/topics-122237.html

    PureComponent :
    解决无状态组件重复渲染问题。
    你可以实现shouldComponentUpdate函数来指明在什么样的确切条件下,你希望这个组件得到重绘。如果你编写的是纯粹的组件(界面完全由 props 和 state 所决定),你可以利用PureComponent来为你做这个工作。

    扩展:Medium 网站访问不到

    打开 hosts 文件(C:\Windows\System32\drivers\etc\hosts),向其中添加:

    # Medium Start
    104.16.120.127  medium.com
    104.16.120.145  api.medium.com
    104.16.120.145  cdn-static-1.medium.com
    104.16.120.145  cdn-images-1.medium.com
    104.16.120.145  cdn-images-2.medium.com
    # Medium End 
    
    

    你可能感兴趣的:(react 总结和技巧)