Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React/Redux Style Guide #11

Open
linqinghao opened this issue Mar 31, 2018 · 0 comments
Open

React/Redux Style Guide #11

linqinghao opened this issue Mar 31, 2018 · 0 comments
Labels

Comments

@linqinghao
Copy link
Owner

简要

  • JSX规范
  • 组件规范
  • 组件分类
  • 组件通信
  • Redux规范
  • 更新时间
  • 参考

JSX规范

  • 建议 对齐方式

    // 规则:react/jsx-closing-bracket-location react/jsx-closing-tag-location
    // bad
    <Hello
    lastName="Smith"
    firstName="John" />;
    
    <Hello
    lastName="Smith"
    firstName="John"
    />;
    
    // good 若能在一行中显示, 直接写成一行
    <Hello firstName="John" lastName="Smith" />;
    
    // good 有多行属性的话, 新建一行关闭标签
    <Hello
    firstName="John"
    lastName="Smith"
    />;
  • 强制 自关闭标签需要加一个空格

    // 规则:no-multi-spaces, react/jsx-tag-spacing
    // bad
    <Foo/>
    <Foo            />
    <Foo
    />
    
    // good
    <Foo />
  • 建议 能用三元运算符,就不用if else, 能用&&就不用三元运算符

    // bad
    {
        if(this.state.isUserLogin) {
            return (
                <MyComp />
            )
        } else {
            return null
        }
    }
    
    // not bad
    { this.state.isUserLogin ? <MyComp /> : null }
    
    // good
    { this.state.isUserLogin && <MyComp /> }
  • 建议 代码块使用多行注释,语句使用单行注释,参数说明使用代码后注释

    /**
     * desc: Modal弹窗组件
     * usage: Modal.open('')
     * author: Allin.
     * update: 2017/12/01
     */
    import React from 'react';
    
    const Header = (props) => (
        <header>{props.title}<header/>
    )
    
    export defalt class Modal extends React.Component {
        constructor() {
            this.state = {
                isdisplay: false,  // modal display or not 
            }
        }
        render() {
            return (
                <div>
                    {/* header */}
                    <Header />
                </div>
            )
        }
    }
  • 必须 JSX中属性用"",js中用''

    // bad
    <Foo bar='bar' />
    
    // good
    <Foo bar="bar" />
    
    // bad
    <Foo style={{ color: "red" }} />
    
    // good
    <Foo style={{ color: 'red' }} />
  • 建议 使用classnames包替代jsx中的样式处理逻辑

    // bad
    render() {
        return (
        	<div classname={this.props.type ? `base-style base-style-${this.props.type}` : 'base-style'}>
            	{this.props.children}
          	</div>
        )
    }
    
    // good
    import classnames from 'classnames'
    render() {
      	const { type } = this.props;
      	const cls = classnames({
          	'base-style': true,
            [`base-style-${type}`]: this.props.type
        })
        return <div className={cls}>{this.props.children}</div>
    }

组件规范

  1. 命名
    • 建议 首字母大写,尽量要求一目了然,可以借鉴CSS BEM的思想,BlockElementModify的命名方式
      // bad
      <isHeader />
      
      // good
      <UserHeader />
  2. 组件写法
    • 强制 采用ES6class写法来创建React Component

    • 建议 文件名用jsx后缀

    • 建议 每个文件包含一个组件,Stateless Component可以例外

    • 建议 使用prop-types规定每个可接受属性的类型,并对 propTypes 加以说明,设置默认

      // 必须使用 Class 的形式创建组件
      // 规则:'react/prefer-es6-class': ['error','always']
      // @off 不强制要求写 propTypes,只是建议
      // 规则:'react/prop-types': 'off'
      import React from 'react';
      import { string, number, func } from 'prop-types';
      class UserCardInfo extends React.Component {
          static PropTypes = {
              username: string.isRequired,
            	age: number.isRequired,
            	handleClick: func
          }
      	render() {
              return ...
          }
      }
    • 必须 使用camalCase来命名props

      // bad
      <UserList user_data={data} />
      
      // good
      <UserList userData={data} />
    • 建议props的值为true时,省略={true}

      // 规则: react/jsx-boolean-value
      // bad
      <Modal hidden={true} />
      
      // good
      <Modal hidden />
    • 建议 React组件内部方法顺序排列方式(统一ES6写法)

      import React from 'react';
      import PropTypes from 'prop-types';
      class NavHeader extends React.Component {
          constructor(props) {
              super(props);
              this.state = {
                  isActive: false
              }
          }
      
          static PropTypes = {}
      
          static DefaultProps = {}
      
          // 组件生命周期方法
          componentWillMount() {}
      
          componentDidMount() {}
      
          componentWillReceiveProps() {}
      
          shouldComponentUpdate() {}
      
          componentWillUnmount() {}
      
          componentDidCatch() {} // React16新增,用于捕获组件错误
      
          // 组件内部方法
          handleClick() {}  
      
          ...
      
          render() {}
      
      }
    • 必须 仅在state中保留必要的组件运行时状态,保持state的简洁性

    • 必须 仅在实例化生命周期中绑定window事件

    • 必须 在组件销毁期解绑window事件

      componentDidMount() {
          window.addEventListener('click', this.handleClick);
      }
      componentWillUnmount() {
          window.removeEventListener('click', this.handleClick);
      }
    • 建议 使用扩展操作符向子组件传递属性

      // bad
      <UserList 
          userList={this.state.userList}
          isLogin={this.state.isLogin}
          paganation={this.state.paganation}
      >
      
      // good
      render() {
          const props = {
              userList: this.state.userList,
              isLogin: this.state.isLogin,
              paganation: this.state.paganation
          };
      
          return <UserList {...props}>;
      }
    • 必须 总是在Refs里使用回调函数

      // 禁止使用字符串 ref
      // 规则: 'react/no-string-refs': 'error',
      // bad
      <UserInfo ref="userInfo" />
      // good
      <UserInfo ref={(node) => this.userInfo=node; } />
    • 建议 尽可能不使用context,绝大多数的应用程序不需要使用context,它虽然提供了便利可以访问组件树中任一节点的上下文,但是违背了单向数据流和自顶向下的原则。context一个实验性的API,在将来可能会被废弃掉。关于是否应该使用context,可以阅读react官方的说法。

组件分类

组件主要分为通用组件业务组件

通用组件

  • 与业务无关,组件独立
  • 只负责UI展示、交互事件
  • 尽量少的依赖,保持复用性
  • 不依赖父组件,用过自定义事件与之交互
  • 数据扁平化,使用propTypes校验接收到的数据

业务组件:

  • 获取数据
  • 数据处理
  • 引用可复用组件

react

组件通信

  1. 父子组件通信
    • 父组件传递props给子组件,子组件通过events给父组件发送信息,遵循单向数据流动与自顶向下的原则。(嵌套太深时,不使用)
    • 使用PubSub模式(利于解耦)
  2. 兄弟组件通信
    • 考虑state提升,通过公用父组件通信
    • 使用PubSub模式
  3. 非父子、兄弟组件通信
    • 使用PubSub模式
    • 使用Flux实现

Redux规范

Redux结构

- store
	- actions
		- user.js
	- reducers
		- user.js
	- constants
		- user.js

actionsreducersconstants建议一一对应,方便查找与维护。

  1. actions 存放同步action和异步action

  2. reducers 存放对应store的CURD操作

  3. constants 存放对应action的常量名词,便于维护与查看

    // actions/user.js
    import * as usersConst from 'constants/user';
    // async action
    export const login = () => async (dispatch) => {
        // login handle
        return {
            type: loginSuccess,
            data
        }
    }
    // sync action
    export const loginSuccess = (data) => ({
        type: usersConst.LOGIN_SUCCESS,
        data
    })
    
    // reducers/user.js
    import { createStore } from 'redux';
    import * as usersConst from 'constants/user';
    
    function user(state={}, action) {
        switch(action.type) {
            case usersConst.loginSuccess:
            return { ...action.data };
            default:
            return state;
        }
    }
    
    export let user = createStore(user);
    
    // constants/user.js
    export const loginSuccess = 'LOGIN_SUCCESS';
    export const loginError = 'LOGIN_ERROR';
    ...

Redux 写法

  • 建议 尽量使用简写方式,多用箭头函数和... 操作符

    // not bad
    export function login(data) {
        return async function(dispatch, getState) {
    		// do something
        }
    }
    
    // good
    export const login = (data) => async (dispatch, getState) => {
        // do something
    }
    
    // not bad
    function user(state={}, action) {
        switch(action.type) {
          	case usersConst.loginSuccess:
            return Object.assign({}, state, action.data);
          	default:
            return state;
        }
    }
    
    // good
    function user(state={}, action) {
        switch(action.type) {
          	case usersConst.loginSuccess:
            return {...state, ...action.data};
          	default:
            return state;
        }
    }
  • 建议 使用connect时使用简写

    // not bad
    import { connect } from 'react-redux'
    // A functional React component
    function App ({ message, onMessageClick }) {
      return (
        <div onClick={() => onMessageClick('hello')}>
          {message}
        </div>
      )
    }
    // Maps `state` to `props`:
    // These will be added as props to the component.
    function mapState (state) {
      return { message: state.message }
    }
    
    // Maps `dispatch` to `props`:
    function mapDispatch (dispatch) {
      return {
        onMessageClick (message) {
          dispatch({ type: 'click', message })
        }
      }
    }
    
    // Connect them:
    export default connect(mapState, mapDispatch)(App)
    
    // ok
    export default connect(
    	(state) => ({
    		message: state.message
      	}),
        (dispatch) => ({
        	onMessageClick: (message) => {
        		dispatch({ type: 'click', message })
        	}
        })
    )(App)
  • 建议 仅在容器组件中使用connect,将数据传递给展示组件,权责分明,方便维护

  • 优化 使用 redux-action减少样板代码

    // actions
    import { createAction } from 'redux-action';
    
    /*
     * action create
     */
    
    export const fetchDataStart = createAction();
    export const fetchDataSuccess = createAction(payload => payload);
    
    /**
     * async action create
     */
    export const getList = () => async (dispatch) => {
        // 请求开始
        dispatch(fetchDataStart());
        const res = await api.request();
        // 请求成功
        dispatch(fetchDataSuccess(res));
        
    }
    
    // reducers
    import { combineReducers } from 'redux'
    import { createReducer } from 'redux-action';
    import { fetchDataStart, fetchDataSuccess} from '../actions/list'
    
    let state = {};
    const list = createReducer(state, {
        [fetchDataStart]: (state) => ({state}),
        [fetchDataSuccess]: (payload) => ({...state, ...payload})
    })
    /**
     * reducer compose
     */
    const store = combineReducers({list})
    export default store
  • 优化 state范式化,更加合理的规范化state的存储结构。

更新时间

  • v1.0: 2017/12/11

参考

  1. ReactJS代码规范
  2. Redux文档
  3. Airbnb React/JSX Style Guide
  4. Awesome-redux
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant