react 踩坑记

预览权限不能编辑

项目中是个图,如果控制里面的节点很麻烦,那么久想办法遮罩,最简单的方式就是使用伪类,但是伪类用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

原生标签可以传入children

antd的table可编辑行的代码

const EditableRow = ({ form, index, ...props }) => (
  <EditableContext.Provider value={form}>
    <tr {...props} />
  </EditableContext.Provider>
);

乍看一脸懵逼,props里包含children居然在tr里渲染出来了,写测试代码

export default class Parent extends React.Component {
  render() {
    return (
      <div>
        <p>xxx</p>
        <div
          children={
            <div>
              <span>geek</span> <span>jin</span>
            </div>
          }
        />
      </div>
    );
  }
}
原生标签的children属性

是有作用的

高阶组件传递ref

想拿到最里层组件的引用还是修改高阶组件的代码,所以第三方的高阶组件一般都会提供withRef参数去拿被包裹的组件,比如 react-i18next

子组件

import { translate } from "react-i18next";

@translate("", { withRef: true })
export default class CepModal extends React.Component {}

父组件

let cepRef = React.createRef();

function onCepLinkClick(e) {
  const cepModalInstance = cepRef.current.getWrappedInstance();
  cepModalInstance.showModal();
}

function CustomDefaultTemplate(props) {
   return <CepModal ref={cepRef}  />
}

React 高阶组件传递Forwarding Refs

antd Menu 里的MenuItem 不能被高阶组件包裹

通过render props的方式统一管理antd modal的状态

                {this.renderSavePointListModal(
                  ({ handleClick, ...restProps }) => (
                    <Menu.Item {...restProps}>
                      <a onClick={handleClick}>
                        <ZetIcon type="export" />
                        &nbsp;savepoint list
                      </a>
                    </Menu.Item>
                  )
                )}

  renderSavePointListModal = MenuItem => {
    const {
      ui: { savePointList }
    } = this.props;
    return (
      <ModalContainer>
        {(
          { visible, restProps, show, hide } // 将Menu传递过来的props重新传递到MenuItem
        ) => (
          <Fragment>
            <MenuItem handleClick={show} {...restProps} />
            <SavePointListModal
              visible={visible}
              hide={hide}
              savePointList={savePointList}
              deleteSavePoint={this.handleDeleteSavePoint}
            />
          </Fragment>
        )}
      </ModalContainer>
    );
  };

ModalContainer.js

export default class ModalContainer extends React.Component {
  state = {
    visible: false
  };
  show = () => {
    this.setState({ visible: true });
  };
  hide = () => {
    this.setState({ visible: false });
  };
  toggle = () => {
    this.setState(({ visible }) => ({ visible: !visible }));
  };

  render() {
    const { visible } = this.state;
    const { children, ...restProps } = this.props;
    console.info(children);
    return children({
      visible,
      show: this.show,
      hide: this.hide,
      toggle: this.toggle,
      restProps
    });
  }
}

但是在SavePointListModal中删除一个条目后savePointList减少了一个。ModalContainer的render方法并没有被调用,原来是antd官方不支持在MenuItem外包裹组件,只能直接用。

A List of antd's components that cannot work with HOC
Memu内包含不是类型为Memu.Item的子节点

antd的解释

antd form setFieldsValue 无法触发定义在getFieldDecorator中的validator函数

只会在 validateTrigger 定义的时机触发校验 https://ant.design/components/form/#getFieldDecorator's-parameters

手动触发请调用 validateFields

setFieldsValue不会触发已经定义的校验吗,校验没有生效

StrictMode 下 render会执行两次

import React, { useState, useEffect } from "react";
import { useWhatChanged } from "@simbathesailor/use-what-changed";

class Parent extends React.PureComponent {
  render() {
    return (
      <div>
        <p>会死循环吗?</p>
        <Child style={{ color: "red" }} />;
      </div>
    );
  }
}

const Child = ({ style }) => {
  const [localStyle, setLocalStyle] = useState();

  useWhatChanged([style]);

  useEffect(() => {
    console.info("inner");
    setLocalStyle(style);
  }, [style]);

  console.info("xxxx", style);

  return "children";
};

export default Parent;

做一次setState操作会render两次,是react StrictMode故意在开发模式下这么设计的

import React from "react";
import ReactDOM from "react-dom";

import App from "./App";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  rootElement
);

StrictMode 是一个用来突出显示应用程序中潜在问题的工具。官方解释

React.lazy 代码分割 报错

react-router@4.3.1

react 官网例子

import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);

我这么用了以后报了个错:Failed prop type: Invalid prop component of type object supplied to Route, expected function.
解决办法:使用render函数传递function Failed prop type: Invalid prop component of type object supplied to Route, expected function

// imports here
... 
const Decks = React.lazy(() => import('./pages/Decks'));
...
class App extends Component {
      ...

    render() {
        return (
            <ConnectedRouter history={history}>
                <div>
                    <MenuAppBar />

                    <div style={{paddingTop: '4rem'}}>
                        <Suspense fallback={<LazyLoading />}>
                            <Switch>
                                <Route exact path="/" component={Home} />
                                <Route path="/decks" render={(props) => <Decks {...props} />} />
                                ...
                            </Switch>
                        </Suspense>
                    </div>

                    <Footer />
                </div>
            </ConnectedRouter>
        );
    }
... 
}

antd table 长列 显示省略号

antd@3.16.3 还没有ellipse属性

  .ant-table-row-level-0 td:nth-child(5) {
      max-width: 26vw;
      word-break: break-all;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
  }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 文/晴天过后上一章 目录 “哇,夏姐姐,你好厉害!”木颜一拍手,兴奋地大喊着。 雨娴留意着曹进那边...
    晴天过后阅读 626评论 4 19
  • 老美每个城市都有飞机场,飞一两个小时到达离你不远的地方是个非常简单的事。可是,如果你选择火车前往,你会感觉到你不是...
    hannah001阅读 212评论 0 0
  • 都说夜里不懂白天的寂寞 而又有谁知凌晨与深夜的交接那种无奈 坐在窗前,无非是睹物思人 不管你在哪,你永远都是我最思...
    aha参茸滋补鹿副业阅读 373评论 0 1
  • 在职场混,无论你是一名刚开始工作的小白,还是资深的管理人员;都经历过或即将经历管理暴力;这里所说的管理暴力是指:上...
    朝斯夫阅读 880评论 0 1