react 实践

1. es6扩展运算符将组建嵌套的dom渲染到组件内部

查看react-component/dialog的时候发现了直接使用...props将组件的嵌套标签渲染到组件内部的情况,做了如下实验。

PaperItem.tsx

import * as React from "react";

export interface Props {
  name: string;
  enthusiasmLevel?: number;
  onIncrement?: () => void;
  onDecrement?: () => void;
}

export default function PaperItem(props: Props) {
  return <div className="paper-item" {...props} />;//使用扩展运算符
}

PaperList.tsx

import * as React from "react";
import PaperItem from "../components/PaperItem";

export default class PaperList extends React.Component {
  data = {
    name: 99
  };
  render() {
    return (
      <div>
        {[1, 2, 3, 4].map(x =>
          <PaperItem key={x} name={String(x)}>
            <h1>ii</h1>//将这两行插入到PaperItem里面
            <h2>oo</h2>
          </PaperItem>
        )}
      </div>
    );
  }
}

渲染结果:


name props会渲染到展开他的标签上,而children就作为该标签的子节点渲染出来了,通常是使用tihs.props.children拿到组件里面嵌套的标签,然后遍历出来,这么写的确是一条捷径。
vue有slot可以轻松做到嵌套的dom,渲染到组件内部,命名slot的占位作用可以准确的定位要插入的位置和数量,react这边有待发现。
昨晚查阅了react官方文档,在组合 vs 继承章节,详细说明了props.children的使用和类似vue slot的使用,类似slot的占位和多个外部dom或者组件片段插入组件,react是通过props实现的,props可以传递基础数据类型,函数,原生的dom,或者组件。这样就很方便了。


2. 组件类型
  • 1.类组件合函数组件(无状态函数组件)
    在官网组件 & Props提到了累组件和函数组件,函数组件没有state没有生命周期函数,所以每次更新都是由外面传入的props的改变引起的,而且渲染速度比类组件要快。一般函数组件作为展示组件使用,展示数据和效果,状态的改变交给外部的类组件,对于redux来说就是容器组件,在react定义d.ts接口的时候定义了如下的接口:
    type SFC<P = {}> = StatelessComponent<P>;
    interface StatelessComponent<P = {}> {
        (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
        propTypes?: ValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
    }

    interface ComponentClass<P = {}> {
        new (props?: P, context?: any): Component<P, ComponentState>;
        propTypes?: ValidationMap<P>;
        contextTypes?: ValidationMap<any>;
        childContextTypes?: ValidationMap<any>;
        defaultProps?: Partial<P>;
        displayName?: string;
    }

    interface ClassicComponentClass<P = {}> extends ComponentClass<P> {
        new (props?: P, context?: any): ClassicComponent<P, ComponentState>;
        getDefaultProps?(): P;
    }

所以在定义一个函数类的时候可以去这么定义:

import * as React from 'react'
export interface LockPropType {
  src: any
  onClick?: (e?: any) => void
}
const Lock: React.StatelessComponent<LockPropType> = ({ src, ...restProps }) => {
  return (
    <div>
    </div>
  )
}
export default Lock

谈一谈创建React Component的几种方式

3. 等待state更新完后,利用更新后的state值去做其他操作

项目里每个有的页面是公用的,不同的路由有公用一个组件。这个时候路由的props match改变了所以走了componentWillReceiveProps钩子函数,这时候需要初始化页面的state,并利用初始化后的参数做ajax请求。那么就可以使用setState(updater, [callback])

this.setState({name:'geek'},()=>{
  request('url',{name:this.state.name}).then()
})

react-component有详细说明

2. Children.only(this.props.children)

在读react-redux/src/components/Provider.js源码的时候遇到了这么一句话

    class Provider extends Component {
        render() {
          return Children.only(this.props.children)
        }
    }

然后结合实际的项目看

import React from 'react'
import { render } from 'react-dom'
import { createStore } from 'redux'
import { Provider } from 'react-redux'
import App from './components/App'
import reducer from './reducers'

const store = createStore(reducer)

render(
  <Provider store={store}>
    <App />//那就是Provider组件只能包含一个根子组件,也就是一个对象,而不是数组
  </Provider>,
  document.getElementById('root')
)

React.Children是react顶层的api其他文档可参考React 顶层 API

3. 使用vscode调试create-react-app

VSCode debugging with Create-React-App

Debugging in the Editor

4. react拖拽生成组件代码

拖拽生成组件,大家react怎么实现?

5. redux-devtools 配置

  • 不安装Chrome redux devtools
    使用 Redux-devTools简单的使用 这种方式会在页面生成redux devtools
  • 安装Chrome redux devtools 那么默认就使用Chrome redux devtools
store = createStore(
        combineReducers(reducers),
        compose(applyMiddleware(thunk),window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()) //插件调试,未安装会报错
    );

使用Redux DevTools浏览器插件调试redux

6. antd table 设置固定在顶部 不对齐的问题

table 设置 scroll.y 后 theader 和 tbody 的 border 无法对齐

able align broken when cell contains long number or long word after 3.11.3

解决办法是

columns={[
  ...
  render: (text, record) => (
    <div style={{ wordWrap: 'break-word', wordBreak: 'break-all' }}>
      {text}
    </div>
  ),
]}

但是还是不能完全解决问题,有时候会提前内容截断

截断

7. antd 事件触发报错

rning: This synthetic event is reused for performance reasons. If you're seeing this,

    <a
                      onClick={this.handleDeleteConfirm("deleteRow")}
                      data-key={record.key}
                    >
                      {t("delete")}
                    </a>

  handleDeleteConfirm = funcName => event => {
    event.persist(); //使用这个就不报错了
    Modal.confirm({
      title: "Do you want to delete these items?",
      onOk: () => {
        // console.info(event);
        this[funcName](event);
      },
      onCancel() {}
    });
  };

不使用 event.persist()的时候 event.target 居然是confirm的确定按钮,按理说是a标签才对,event.persist()的作用通过在回调事件顶部加上 e.persist() 就可以从池中移除合成事件,并允许对事件的引用保留。

在 react 组件中使用 debounce 函数

8. create-react-app@3 运行jest测试报错

首先项目是经过eject,所有的配置都被暴露出来,项目中是使用Absolute Imports的方式引入包
jsconfig.json配置

{
  "compilerOptions": {
    "baseUrl": "src"
  },
  "include": ["src"]
}

然后直接import Button from 'components/Button';不需要用相对路径Absolute Imports,但是在用jest做测试的时候就报错 cannot find module
需要在package.json的jest配置项中配置如下参数

moduleDirectories: ['node_modules', 'src']

Jest + Typescript + Absolute paths (baseUrl) gives error: Cannot find module

9. create-react-app@3 注册全局函数

setupTests.js

import React from "react";
import { configure, shallow, render, mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";

configure({ adapter: new Adapter() });


global.React = React;
global.shallow = shallow;
global.render = render;
global.mount = mount;

在package.json中jest选项配置

    "setupFilesAfterEnv": [
      "<rootDir>/src/setupTests.js"
    ]

这样在测试文件中不需要引入react跟enzyme等文件,直接使用

import Temperature from "./Temperature";

it("renders correctly", () => {
  const wrapper = shallow(
    <Temperature temp={10} city="Toronto" toggleForecast={() => {}} />
  );

  expect(wrapper).toMatchSnapshot();
});

使用 Jest 和 Enzyme 测试 React 组件
Testing React with Jest, Enzyme, and Sinon
Using enzyme with Jest

但是commit的时候通不过eslint的检测,


error
{
    "globals": {
        "shallow": true,
        "render": true,
        "mount": true
    }
}
  • 针对 error 'React' must be in scope when using JSX react/react-in-jsx-scope参考How to use ESLint with Jest 安装eslint-plugin-jest,然后配置
    "overrides": [
      {
        "files": [
          "**/*.test.js"
        ],
        "env": {
          "jest": true
        },
        "plugins": [
          "jest"
        ],
        "rules": {
          "react/react-in-jsx-scope": "off"
        }
      }
    ],
  1. css modules 下覆盖antd的样式
    then.less
.stopRuleSet {
  .ant-radio {
    display: none !important;
  }
}

然后radio的样式并没有生效,编译为

.then_stopRuleSet__35L3K .then_ant-radio__oQkAO {
  display: none !important;
}

.ant-radio的样式被也被加了hash值,标签里的classname没加hash


classname

所以less文件中的classname也不应该被加上hash值,加上:global就好了

.stopRuleSet {
  :global {
    .ant-radio {
      display: none !important;
    }
  }
}

antd pro 修改样式

10. react-router 获取上一次路径

vue-router的beforeRouteEnter beforeRouteUpdate beforeRouteLeave 导航守卫的from参数是上个路由的。详见导航守卫

但是react-router@4 是没有这些钩子函数的。Detect previous path in react router? 给了我们一些方式

  • You can pass down state using the <Link> component, in this case a pathname:

<Link to={{pathname: '/nextpath', state: { prevPath: location.pathname }}}>Example Link</Link>

You can then access prevPath from this.props.location.state in the next component

直接用push传递state也可以

  • If you're using react-router-redux you can create a reducer which hooks into the events dispatched by react-router-redux.
export default function routerLocations(state = [], action) {
  switch (action.type) {
    case "@@router/LOCATION_CHANGE":
      return [...state, action.payload]
    default:
      return state;
  }
}

自定义reducer在路由跳转触发dispatch抛出的@@router/LOCATION_CHANGE action,记录每次路由的变化

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

推荐阅读更多精彩内容