react+redux+router入门总结

react+redux+router入门总结

目录

  1. 构建配置
  2. React组件、css module
  3. React Router 使用
  4. Redux Redux 使用
  5. 注意问题
  6. 资料整理

一、构建配置

1)使用 react-app-rewired 对 create-react-app 的默认配置进行自定义

1、 引入 react-app-rewired 并修改 package.json 里的启动配置。由于新的 [email protected] 版本的关系,你还需要安装 customize-cra

yarn add react-app-rewired customize-cra babel-plugin-import less less-loader --dev
/* package.json */
"scripts": {
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",
-   "test": "react-scripts test",
+   "test": "react-app-rewired test",
}

2、根目录创建 config-overrides.js

const path = require('path');
const { override, fixBabelImports, addLessLoader, addWebpackAlias } = require('customize-cra');

const addWebpackConfig = () => (config) => {
  if (process.env.NODE_ENV === 'production') {
    config.devtool = false
    config.output.publicPath = '//static.kuaizi.co/super-recommend/'
  }

  return config
}

module.exports = override(
  // 按需加载
  fixBabelImports('lodash', {
    libraryDirectory: '',
    camel2DashComponentName: false
  }),
  // 按需加载
  fixBabelImports('import', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: true
  }),
  addLessLoader({
    noIeCompat: true,
    javascriptEnabled: true,
    localIdentName: '[local]--[hash:base64:5]', // 开启less module
    modifyVars: { // less 变量
      '@primary-color': '#0999aa',
      '@success-color': '#45A767',
      '@layout-header-background': '#0999aa'
    }
  }),
  addWebpackAlias({
    "@": path.resolve(__dirname, "src")
  }),
  addWebpackConfig()
)

2)PUBLIC_URL

1、添加环境变量到build, dev访问需要配合proxy

/* package.json */
"scripts": {
  "build": "PUBLIC_URL=//xxx.cn react-app-rewired build",
}

2、使用

index.html


JavaScript

render() {
    return ;
}

3)proxy

1 安装http-proxy-middleware依赖

yarn add http-proxy-middleware --dev

2 在src目录下新建 setupProxy.js

const proxy = require("http-proxy-middleware");

module.exports = function(app) {
  app.use(
    // api代理
    proxy("/api", {
      target: "http://xxx.cn",
      secure: false,
      changeOrigin: true,
      pathRewrite: {
         "^/api": ""
      }
    }),

    // cnd资源代理
    proxy("/common", {
      target: "http://xxx.cn",
      secure: false,
      changeOrigin: true
    })
  );
};

二、React组件

1)有状态组件 class component

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  render() {
    return (
      

You clicked {this.state.count} times

); } }

2)无状态函数组件

import PropTypes from 'prop-types'
const Example = props => {
  const { count, onClick } = props;
  return (
    

You clicked {count} times

) } Example.propTypes = { count: PropTypes.number, onClick: PropTypes.func, } Example.defaultProps = { count: 0, onClick: (() => {}) }

3) HOC高阶组件

HOC(High Order Component) 是 react 中对组件逻辑复用部分进行抽离的高级技术,但HOC并不是一个 React API 。 它只是一种设计模式,类似于装饰器模式。

在 Vue 中通常我们采用: mixins

const DataStorageHoc = WrappedComponent => {
  class DataStorage extends React.Component{
    state = {
      data: null
    }
    
    componentWillMount() {
      const data = localStorage.getItem('data')
      this.setState({ data })
    }

    render() {
      const { forwardedRef, ...rest} = this.props
      // 2. 我们接收到 props 中被改名的 forwardedRef 然后绑定到 ref 上
      return  
    }
  }

  return React.forwardRef((props,ref)=>{
     // 1. 我们接收到 ref 然后给他改名成 forwardedRef 传入到props中, 因为此ref是保留字段,需要dom元素才能接收
    return 
  })
}

使用

// example.jsx
import DataStorageHoc from './hoc/data-storage.jsx'

// wrapped component
class Example extends React.Component{
  echo = () => {
    console.log('hello')
  }
  render () {
    return 

{this.props.data}

} } export default DataStorageHoc(Example) // ================================ // 装饰器(decorator)模式 @DataStorageHoc class Example extends React.Component{ echo = () => { console.log('hello') } render () { return

{this.props.data}

} } export default Example // ================================ // 调用 // app.jsx class App extends React.Component { constructor(props){ super(props) this.exampleRef = React.createRef() } handleEcho = () => { this.exampleRef.current.echo() } render() { return (
) } } export default App

4) hooks是有状态的函数

hook作用: 将可复用的最小单元从组件层面进一步细化到逻辑层面。状态逻辑和UI是解耦的。

import { useState } from 'react'
const Example = () => {
    const [count, setCount] = useState(0);
    return (
      

You clicked {count} times

); }

5) 自定义hooks

function useCount(initialValue = 0) {
  const [count, setCount] = useState(initialValue)
  useEffect(() => {
    service.getInitialCount().then(data => {
      setCount(data)
    })
    return () => {
     console.log('计数完成')
    }
  }, [])
  function addCount() {
    setCount(c => c + 1)
  }
  return { count, addCount }
}

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth)

    window.addEventListener('resize', handleResize)

    return () => {
      window.removeEventListener('resize', handleResize)
    }
  })

  return width
}

const App = () => {
  const { count, addCount } = useCount(0)
  const width = useWindowWidth()

  const onChange = (e) => {
    handleNameChange(e.target.value)
  }
  
  return (
    

window width {width}

You clicked {count} times

) }

6) css动态样式功能

classnames

1、安装

yarn add classnames

2、使用方法

import classnames from 'classnames'
 
// 其他类型 classNames('foo', 'bar'); // => 'foo bar' classNames('foo', { bar: true }); // => 'foo bar' classNames({ 'foo-bar': true }); // => 'foo-bar' classNames({ 'foo-bar': false }); // => '' classNames({ foo: true }, { bar: true }); // => 'foo bar' classNames({ foo: true, bar: true }); // => 'foo bar'

7) less module

  • 文件名规则 xxx.module.less, 必须以.module.less结尾
  • 命名:可以使用驼峰或者连接线命名

index.module.less

.list {
  height: 100%;
  overflow-y: auto;
}
.listItem {
  height: 50px;
}

.itemActive {
  color: aqua;
}

.item-disabled {
  color: #999;
}

使用样式

import classNames from 'classnames'
import styles from './index.module.less'

const Example = props => {
  return (
    
  • ...
  • ...
) }

三、React Router

1) 安装

创建 Web应用,使用

yarn add react-router-dom

创建 navtive 应用,使用

yarn add react-router-native

2) 路由模式

BrowserRouter模式 创建的 URL 形式如下:

http://example.com/some/path

HashRouter模式 创建的 URL 形式如下:

http://example.com/#/some/path

3) Route组件

组件是react router v4里最有用的组件。无论何时你需要在匹配某个路径的时候绘制一个组件,那么就可以使用Route组件。

Route组件可以使用如下的属性:

  • path属性,字符串类型,它的值就是用来匹配url的。
  • component属性,它的值是一个组件。在path匹配成功之后会绘制这个组件。
  • exact属性,这个属性用来指明这个路由是不是排他的匹配 (绝对匹配)。
  • strict属性, 这个属性指明路径只匹配以斜线结尾的路径。

还有其他的一些属性,可以用来代替component属性

  • render属性,一个返回React组件的方法,只要你的路由匹配了,这个函数才会执行
  • children属性,返回一个React组件的方法。只不过这个总是会绘制,即使没有匹配的路径的时候。

使用component


使用render

 (
    
)} />

使用children


 (
    
children
)} />

exact

http://example.com/#/path
  // 不匹配

非exact

http://example.com/#/path
  // 匹配

4)Switch组件

只有第一个匹配的路由或者会被绘制,匹配到一个就不再匹配

import { Switch, Route } from 'react-router-dom'

  
  
  

5)Link、NavLink、Redirect组件

Link组件需要用到to属性,这个属性的值就是react router要跳转到的地址

NavLink是Link的一个子类,在Link组件的基础上增加了绘制组件的样式

import { Link, NavLink } from 'react-router-dom'

Home




  My Profile


// 重定向

除了使用Link外,我们还可以使用 history 对象手动实现导航。history 中最常用的两个方法是 push(path,[state]) 和 replace(path,[state]),push会向浏览器记录中新增一条记录,replace 会用新记录替换记录。

history.push('/posts')
history.replace('/posts')

6)Router Cache

原理:display "block" "none"

1、安装 react-router-cache-route

yarn add react-router-cache-route --dev

2、使用

import { Route } from 'react-router-dom'
import CacheRoute, { CacheSwitch } from 'react-router-cache-route'

const App = () => {
  return (
    
      
      
      
    
  )
}

7)例子

// app.jsx

import { HashRouter, Route, Switch } form 'react-router-dom'

const MenuLayout = ({ location }) => (
  

React Router v4 Browser Example

404 Not Found
} />
React Router v4 Browser Example (c) 2017
) const App = () => { return ( // 不要使用 exact ) } export default App

四、 Redux Redux

1) 定义reducer

redux/action-type.js

export const SET_USER_INFO = 'SET_USER_INFO'
export const SET_USER_LIST = 'SET_USER_LIST'
export const SET_NEWS_LIST = 'SET_NEWS_LIST'

redux/actions.js

import { SET_USER_INFO, SET_NEWS_LIST, SET_USER_LIST } from './action-type'

export const setUserInfo = (data) => {
  return {
    type: SET_USER_INFO,
    data
  }
}

export const setUserList = (data) => {
  return {
    type: SET_USER_LIST,
    data
  }
}

export const setNewsList = (data) => {
  return {
    type: SET_NEWS_LIST,
    data
  }
}

redux/reducers/user.js

import { SET_USER_INFO, SET_USER_LIST } from './action-type'

const initialState = {
  userInfo: null,
  userList: []
}
const reducer = (state = initialState, action) = {
  switch(action.type){
    case SET_USER_INFO:
      return Object.assign({}, state, {
        userInfo: action.data
      })
    case SET_USER_LIST:
      return Object.assign({}, state, {
        userList: action.data
      })  
    default:
      return state 
  }
}

export default reducer

redux/reducers/news.js

import { SET_NEWS_LIST } from './action-type'

const initialState = {
  newsList: [],
  total: 0
}
const reducer = (state = initialState, action) = {
  switch(action.type){
    case SET_NEWS_LIST:
      return Object.assign({}, state, {
        newsList: action.data.list,
        total: action.data.total
      })
    default:
      return state  
}
export default reducer

2) 合并reducer redux/reducers/index.js

import {combineReducers} from 'redux'
import user from './user'
import news from './news'

export default combineReducers({
  user,
  news
})

3)创建store redux/index.js

import {createStore} from 'redux'
import reducers from './reducers'

// 创建store对象
const store = createStore(reducers)

export default store

4)redux connect, 将react与redux关联起来

connect()接收四个参数,它们分别是mapStateToProps,mapDispatchToProps,mergeProps和options。

  • mapStateToProps 将state映射到 UI 组件的参数(props)

  • mapDispatchToProps 将action映射到 UI 组件的参数(props)

  • mergeProps 选项,如果指定,则定义如何确定自己包装的组件的最终道具。如果不提供mergeProps,则包装的组件默认会收到{...ownProps,...stateProps,...dispatchProps}

  • options 选项

    • [forwardRef = false] 如果已传递{forwardRef:true}进行连接,则向连接的包装器组件添加ref实际上将返回被包装组件的实例。默认为false

    • ...

connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])

5) 使用redux

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import store from './redux'
import App from './App'

ReactDOM.render(
  
    
  
  document.getElementById('root')
);

App.js

import { connect } from "react-redux"
import { setUserInfo } from '@/redux/action'

const App = (props) => {
  const { userInfo, setUserInfo, newsList } = props

  const onClick = () => {
    setUserInfo({
      name: 'oo'
    })
  }

  return (
    

{userInfo.name}

    { newsList.map((o) => { return
  • {o.title}
  • }) }
) } App.defaultProps = { userInfo: {}, newsList: [] } const mapStateToProps = (state) => ({ userInfo: state.user.userInfo, newsList: state.news.newsList }) const mapDispatchToProps = (dispatch) => ({ setUserInfo: (data) => dispatch(setUserInfo(data)) }) export default connect( mapStateToProps, mapDispatchToProps, null, {forwardRef: true} )(App)

五、注意问题

1)ant design Form.create 之后如果拿不到 ref

经过 Form.create 之后如果要拿到 ref,可以使用 rc-form 提供的 wrappedComponentRef,详细内容可以查看这里。

class CustomizedForm extends React.Component { ... }

// use wrappedComponentRef
const EnhancedForm =  Form.create()(CustomizedForm);


class Example extends React.Component {
  render () {
    return (
      this.form = form} />
    )
  }
}

2) redux connect 之后拿不到ref

connect 之后拿不到ref,配置options.forwardRef=true

class Example extends React.Component { ... }

export default connect(
  mapStateToProps,
  mapDispatchToProps,
  null,
  {forwardRef: true}
)(Example)

资料整理

react

create-react-app

react-app-rewired

customize-cra

react-router

react-redux

classnames

你可能感兴趣的:(react+redux+router入门总结)