react处理多次请求_react 踩坑记

预览权限不能编辑

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

className={classnames(`${this.prefix}main-section-wrapper`, {

withafter: !editable

})}

>

{this.renderMainSection()}

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) => (

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) => (

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就会重新装载

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

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

path="/editApp"

exact

component={EditApp}

key={new Date().getTime()}

/>

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

后面尝试了

listenLocation = () => {

this.unlisten = this.props.history.listen(({ pathname, search }) => {

// 拉取已发布数据

});

};

componentWillUnmount() {

this.unlisten();

}

html5 拖拽在Firefox中不生效

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

handleDragStart = (src, dragItem) => e => {

e.dataTransfer.setData("txt", "xxx"); //加入这行代码在Firefox下就可以拖拽了

this.tabLabel = src;

this.dragItem = dragItem;

};

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

解决办法就是使用 ljharb/

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

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

return class extends React.Component {

static displayName = `Model(${getDisplayName(WrappedComponent)})`;

render() {

return ;

}

};

还有一种方式:

return class extends React.Component {

static displayName = `Model(${getDisplayName(WrappedComponent)})`;

render() {

return React.createElement(WrapperComponent, this.props);

// return ;

}

};

渲染的是原组件的元素,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,不可以是一个元素,二者区别参见

axios 会将arguments对象转为字符串

arguments是关键字

{

arguments:{

name:"xxx"

}

}

axios 就会将上面的转化为:

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

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

antd Dropdown placement多个空格会报错

getPopupContainer={() => document.getElementById(appId)}

placement="bottomRight "

overlay={

{editable && (

{t("app..clone")}

)}

{deletable && (

{t("app..delete")}

)}

}

>

鼠标悬浮就会报错:

报错

关于异步请求loading效果

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

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

jsconfig.json:

{

"compilerOptions": {

"jsx": "react"

}

}

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

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

{

"compilerOptions": {

"experimentalDecorators": true,

"emitDecoratorMetadata": true,

"jsx": "react",

"baseUrl": ".",

"paths": {

"*": ["./src/*"]

}

}

}

{

"path-intellisense.mappings": {

"/": "${workspaceRoot}",

"lib": "${workspaceRoot}/lib",

"global": "/Users/dummy/globalLibs"

},

}

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

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

import modelConnect from "../../components/common/modelConnect";

// 下面这样直接引入就行了

import modelConnect from "components/common/modelConnect";

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

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"

}

]

]

},

使用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)

用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

"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);

原生标签可以传入children

antd的table可编辑行的代码

const EditableRow = ({ form, index, ...props }) => (

);

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

export default class Parent extends React.Component {

render() {

return (

xxx

children={

geek jin

}

/>

);

}

}

原生标签的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

}

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

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

{this.renderSavePointListModal(

({ handleClick, ...restProps }) => (

 savepoint list

)

)}

renderSavePointListModal = MenuItem => {

const {

ui: { savePointList }

} = this.props;

return (

{(

{ visible, restProps, show, hide } // 将Menu传递过来的props重新传递到MenuItem

) => (

visible={visible}

hide={hide}

savePointList={savePointList}

deleteSavePoint={this.handleDeleteSavePoint}

/>

)}

);

};

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外包裹组件,只能直接用。

antd的解释

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

手动触发请调用 validateFields

StrictMode 下 render会执行两次

import React, { useState, useEffect } from "react";

import { useWhatChanged } from "@simbathesailor/use-what-changed";

class Parent extends React.PureComponent {

render() {

return (

会死循环吗?

;

);

}

}

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(

,

rootElement

);

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

React.lazy 代码分割 报错

[email protected]

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 = () => (

Loading...

}>

);

我这么用了以后报了个错: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 (

}>

} />

...

);

}

...

}

antd table 长列 显示省略号

[email protected] 还没有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;

}

你可能感兴趣的:(react处理多次请求)