美文网首页react
react 踩坑记

react 踩坑记

作者: 别过经年 | 来源:发表于2018-06-06 11:30 被阅读357次
    预览权限不能编辑

    项目中是个图,如果控制里面的节点很麻烦,那么久想办法遮罩,最简单的方式就是使用伪类,但是伪类用js是不好控制的,那么就多加减class去处理

              <section
                className={classnames(`${this.prefix}main-section-wrapper`, {
                  withafter: !editable
                })}
              >
                {this.renderMainSection()}
              </section>
    

    css

      &main-section-wrapper {
        position: relative;
      }
    
      &main-section-wrapper.withafter:after {
        content: " ";
        width: 100%;
        height: 100%;
        position: absolute;
        top: 0;
        z-index: 10;
        left: 0;
      }
    

    这样就达到了遮罩的目的

    key 真的很关键

    列表页 有搜索 可以删除每一个item,搜索是antd form getFieldDecorator 那么每次输入字符 list组件都会刷新,但是我们每个item并不需要刷新

     rednerAppItems = (list = [], isPublished) => {
        return list.map((item, idx) => (
          <AppItem
            key={idx}
            data={item}
          />
        ));
      };
    

    一开始我使用数组的idx作为可以赋给每个item的key,但是这样在删除的时候,后面的一个item的key就变成了被删除的item的key,react比较认为哪个被删除的item还存在,后面的item的内容就变成了被删除的那条item的内容,所以需要唯一的key,而不是数组的idx,后来做了调整,使用uuid/v4,生成唯一的key给每个item,但是我是这么操作的:

     rednerAppItems = (list = [], isPublished) => {
        return list.map((item, idx) => (
          <AppItem
            key={uuid()}
            data={item}
          />
        ));
      };
    

    这样每次输入字符到search box,key都是不同的,这样react就认为改item是个新的节点, 每个item都会重新加载,就是走了componentDidMount,这开销就大了,导致我输入卡顿,体验极差,又重新切换到数组的idx,这样搜索的时候不会再走componentDidMount,但是会走shouldComponentUpdate,输入也有些卡顿,依然会render,那么就需要在shouldComponentUpdate做判断,数据没更新,就不需要render,

      shouldComponentUpdate(nextProps, nextState) {
        if (data === this.props.data) {
          return false;
        }
        return true;
      }
    

    针对第一点解决办法是在list的元素中放置一个uuid,绑定到item的key上,这样就不再卡顿,key真的很重要,
    不写shouldComponentUpdate且key是list中的uuid,代码依然走componentDidUpdate,不是太理解,因为我的数据和key都没有变,react的diff算法应该判断dom是没有更新的,就不应该走render去update节点,这点不是很理解,list是托管在redux-ui的,reudx-ui使用了immutable.js 不知道每次数据不变但是appitem update是不是跟这个有关系

    父组件state更新,在三个组件没有传入props的情况下,Component组件,函数组件都会更新,PureComponent组件不会更新,PureComponent是react 16.4.1用shallowEqual做了浅比较

    但是出现个疑问,输入字符进行搜索的时候或者删除操作后,会走componentDidMount,这个时候list数量改变了,虽然每个key没变,但是list数量变了,每个item就会重新装载

    React Render Array 性能大乱斗

    同一个路由组件,路由变化,重新渲染

    项目分为未发布和已发布状态,点击已发布需要跳转到已发布的界面,但是他们共用一个组件,只是路由的query string 不同,发布后请求的接口也是不同的,并且接口请求是在componentDidMount钩子里,在Route在加上实时的key就行了

    <Switch>
        <Route
          path="/editApp"
          exact
          component={EditApp}
          key={new Date().getTime()}
        />
      </Switch>
    

    上面结果尝试失败,key是活的话,路由组件componentDidMount会执行两次
    后面尝试了

      listenLocation = () => {
        this.unlisten = this.props.history.listen(({ pathname, search }) => {
         // 拉取已发布数据
        });
      };
    
     componentWillUnmount() {
        this.unlisten();
      }
    

    退出到list页面也会走listen函数
    react-router4怎么在路由变化时重新渲染同一个组件?

    html5 拖拽在Firefox中不生效

    在chrome下拖拽完全没没问题的,但是在Firefox下挂了

      handleDragStart = (src, dragItem) => e => {
        e.dataTransfer.setData("txt", "xxx"); //加入这行代码在Firefox下就可以拖拽了
        this.tabLabel = src;
        this.dragItem = dragItem;
      };
    

    h5文件拖拽的问题
    关于HTML5拖放中dataTransfer.setData()的问题。

    query-string 在crea-react-app 无法build

    Failed to minify the code from this file

    解决办法就是使用 ljharb/qs

    高阶组件 复制原组件的静态方法到新组件

    最简单的办法:使用hoist-non-react-statics
    react-redux也是使用这种方式拷贝原组件的静态方法

    属性代理的方式返回新的组件的render函数中是要渲染原组件的

      return class extends React.Component {
        static displayName = `Model(${getDisplayName(WrappedComponent)})`;
        render() {
          return <WrapperComponent {...this.props} />;
        }
      };
    

    还有一种方式:

      return class extends React.Component {
        static displayName = `Model(${getDisplayName(WrappedComponent)})`;
        render() {
          return React.createElement(WrapperComponent, this.props);
          // return <WrapperComponent {...this.props} />;
        }
      };
    

    渲染的是原组件的元素,react-redux就是采用第二种方式

          addExtraProps(props) {
            if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
            // make a shallow copy so that fields added don't leak to the original selector.
            // this is especially important for 'ref' since that's a reference back to the component
            // instance. a singleton memoized selector would then be holding a reference to the
            // instance, preventing the instance from being garbage collected, and that would be bad
            const withExtras = { ...props }
            if (withRef) withExtras.ref = this.setWrappedInstance
            if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
            if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
            return withExtras
          }
    
          render() {
            if (this.state.error) {
              throw this.state.error
            } else {
              return createElement(WrappedComponent, this.addExtraProps(this.state.props))
            }
          }
    

    原本以为cloneElement也可以实现,查阅文档发现cloneElement只能根据元素clone出元素,而createElement是根据给定的类型创建并返回新的 React element,不可以是一个元素,二者区别参见

    React文档(二十四)高阶组件

    axios 会将arguments对象转为字符串

    arguments是关键字

    {
     arguments:{
      name:"xxx"
     }
    }
    

    axios 就会将上面的转化为:

    {arguments:"{"name":"xxx"}"}
    

    目前的解决办法是service.interceptors.request.use再转回来

    antd Dropdown placement多个空格会报错

                 <Dropdown
                      getPopupContainer={() => document.getElementById(appId)}
                      placement="bottomRight "
                      overlay={
                        <Menu className={`${this.prefix}header-dropdown`}>
                          {editable && (
                            <Menu.Item>
                              <a onClick={this.cloneItem}>{t("app..clone")}</a>
                            </Menu.Item>
                          )}
                          {deletable && (
                            <Menu.Item>
                              <a onClick={this.removeItem}>{t("app..delete")}</a>
                            </Menu.Item>
                          )}
                        </Menu>
                      }
                    >
                      <a className="ant-dropdown-link">
                        <ZetIcon type="ellipsis" />
                      </a>
                    </Dropdown>
    

    鼠标悬浮就会报错:

    报错

    关于异步请求loading效果

    并不是所有的异步请求都要加loading,大部分是需要的,loading效果用的是antd Spin组件

    React/Redux Tips: Better Way to Handle Loading Flags in Your Reducers

    vscode(version 1.25.1) import jsx 无法转到定义(go to definition)

    jsconfig.json:

    {
        "compilerOptions": {
            "jsx": "react"
        }
    }
    

    Go to definition for .jsx does not work

    vscode(version 1.25.1) import 绝对路径 无法转到定义(go to definition)

    最好的办法是在项目根目录下加上jsconfig.json,然后配置

    {
      "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "jsx": "react",
        "baseUrl": ".",
        "paths": {
          "*": ["./src/*"]
        }
      }
    }
    

    另一种就是使用Path Intellisense插件,然后配置

    {
        "path-intellisense.mappings": {
            "/": "${workspaceRoot}",
            "lib": "${workspaceRoot}/lib",
            "global": "/Users/dummy/globalLibs"
        },
    }
    

    Peek/Go To Definition & Click to Open do not work with Webpack alias

    在create-react-app中使用绝对路径

    在项目根目录的.env文件中加上NODE_PATH=src/,然后src目录下的文件相互引用就不需要加上../../这么长的字符串了,比如

    import modelConnect from "../../components/common/modelConnect";
    
    // 下面这样直接引入就行了
    import modelConnect from "components/common/modelConnect";
    

    并且输入components,vscode还有自动提示,贼六

    Create-React App的使用绝对导入

    eject后按需加载antd

    package.json

      "devDependencies": {
        "babel-plugin-import": "^1.8.0",
        "babel-plugin-transform-decorators-legacy": "^1.3.5",
        "less": "^3.8.1",
        "less-loader": "^4.1.0"
      },
      "babel": {
        "presets": [
          "react-app"
        ],
        "plugins": [
          "transform-decorators-legacy",
          [
            "import",
            {
              "libraryName": "antd",
              "libraryDirectory": "es",
              "style": "css"
            }
          ]
        ]
      },
    

    使用Eject方式在 create-react-app 中使用 Ant Design of React

    使用table的时候报了个错
    *./node_modules/antd/es/spin/style/index.css
    Module build failed:

    /* autoprefixer: off /
    filter: progid:DXImageTransform.Microsoft.Blur(PixelRadius=1, MakeShadow=false);
    ^
    Unrecognised input
    in d:\work\uumsx.web\node_modules\antd\es\spin\style\index.css (line 99, column 63)

    An error occurred using the list component, ./node_modules/antd/es/spin/style/index.css Module build failed: Syntax Error #9518
    把less和css的webpack配置分开写就好了

    用lint-staged检测更改的代码,限制提交

      "eslintConfig": {
        "extends": "react-app",
        "rules": {
          "no-console": [
            "warn",
            {
              "allow": [
                "warn",
                "error"
              ]
            }
          ]
        }
      },
    

    配置eslint不让src下有console.log,但是在配置好后,每次提交有console的代码lint-staged跑都会通过,原来lint-staged只会捕获报错的信息,warning就会通过,尝试把warn改为error,但是项目就直接报错,跑不起来,所以这种方式不行

      "rules": {
          "no-console": [
            "error",
            {
              "allow": [
                "warn",
                "error"
              ]
            }
          ]
        }
    

    然后搜了下
    --max-warnings can serve this purpose: --max-warnings=0 means that any warnings cause eslint to report an error.
    --strict for counting warnings as errors

    --max-warnings=0这样eslint检测的时候,最大warning数为0个,就是不容许有warning,结合Configuring linter, git hooks and auto-format to improve our development workflow做出以下配置

      "scripts": {
        "start": "node scripts/start.js",
        "build": "node scripts/build.js",
        "test": "node scripts/test.js --env=jsdom",
        "precommit": "lint-staged",
        "lint": "eslint --max-warnings 0 src"
      },
      "eslintConfig": {
        "extends": "react-app",
        "rules": {
          "no-console": [
            "warn",
            {
              "allow": [
                "warn",
                "error"
              ]
            }
          ]
        }
      },
      "lint-staged": {
        "*.js": [
          "npm run lint",
          "git add"
        ]
      },
    

    这样的话执行npm run linteslint就会报错,就会被lint-staged捕获到

    eslint报错

    输入校验 debounce

    一开始

    import { debounce } from "lodash";
    
      onWidgetChange = field => e => {
        debounce(this.checkUserName, 500);
      };
    

    然而并不能实现500ms内连续输入不调用接口,而是change触发了几次,就调用了几次接口,原因是:debounce只执行一次,执行后的函数作为事件处理函数

      onNameChange = debounce(this.checkUserName, 500);
    

    实例解析防抖动(Debouncing)和节流阀(Throttling)
    前端性能优化——debounce

    相关文章

      网友评论

        本文标题:react 踩坑记

        本文链接:https://www.haomeiwen.com/subject/zacbsftx.html