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 
;//使用扩展运算符 }

PaperList.tsx

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

export default class PaperList extends React.Component {
  data = {
    name: 99
  };
  render() {
    return (
      
{[1, 2, 3, 4].map(x =>

ii

//将这两行插入到PaperItem里面

oo

)}
); } }

渲染结果:

react 实践_第1张图片

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

= StatelessComponent

; interface StatelessComponent

{ (props: P & { children?: ReactNode }, context?: any): ReactElement | null; propTypes?: ValidationMap

; contextTypes?: ValidationMap; defaultProps?: Partial

; displayName?: string; } interface ComponentClass

{ new (props?: P, context?: any): Component; propTypes?: ValidationMap

; contextTypes?: ValidationMap; childContextTypes?: ValidationMap; defaultProps?: Partial

; displayName?: string; } interface ClassicComponentClass

extends ComponentClass

{ new (props?: P, context?: any): ClassicComponent; getDefaultProps?(): P; }

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

import * as React from 'react'
export interface LockPropType {
  src: any
  onClick?: (e?: any) => void
}
const Lock: React.StatelessComponent = ({ src, ...restProps }) => {
  return (
    
) } 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组件只能包含一个根子组件,也就是一个对象,而不是数组
  ,
  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) => (
    
{text}
), ]}

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

react 实践_第2张图片
截断

7. antd 事件触发报错

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

    
                      {t("delete")}
                    

  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": [
      "/src/setupTests.js"
    ]

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

import Temperature from "./Temperature";

it("renders correctly", () => {
  const wrapper = shallow(
     {}} />
  );

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

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

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


error
  • 对于 error 'shallow' is not defined no-undef参考了 Solving linter error- 'shallow' is not defined no-undef在package.json设置加入
{
    "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


react 实践_第3张图片
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 component, in this case a pathname:

Example 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,记录每次路由的变化

  • 再或者使用第三方库react-router-last-location 该库也支持hooks写法

你可能感兴趣的:(react 实践)