美文网首页
React学习与进阶(更新中)

React学习与进阶(更新中)

作者: nimw | 来源:发表于2018-12-14 19:56 被阅读11次

    1. 环境搭建

    1.1 脚手架工具

    1. 使用create-react-app创建项目
    create-react-app my-react-app
    
    1. 启动项目
    cd my-react-app 
    yarn start
    

    注释:也可以使用npm start启动项目。

    1. mock数据

    ① 创建mock数据文件

    // /public/api/data.json
    {
      "success": true,
      "data": [1, 2, 3]
    }
    

    ② 请求数据

    fetch('/api/data.json')
      .then(res => res.json())
      .then(data => console.log(data))
     // {data: [1, 2, 3], success: true}
    

    2. 新特性

    1. React 中一个常见模式是为一个组件返回多个元素。Fragments 可以让你聚合一个子元素列表,并且不在DOM中增加额外节点。
    class Columns extends React.Component {
      render() {
        return (
          <React.Fragment>
            <td>Hello</td>
            <td>World</td>
          </React.Fragment>
        );
      }
    }
    

    注意:① key 是唯一可以传递给 Fragment 的属性。② 在 React 中, <></><React.Fragment><React.Fragment/> 的语法糖。

    1. Strict Mode严格模式
      StrictMode是一个用以标记出应用中潜在问题的工具。就像FragmentStrictMode不会渲染任何真实的UI。它为其后代元素触发额外的检查和警告。
    import { StrictMode, Component } from 'react'
    
    class Child extends Component {
      // 以下三个函数在 React v16.3 已不被推荐,未来的版本会废弃。
      componentWillMount() {
        console.log('componentWillMount')
      }
      componentWillUpdate() {
        console.log('componentWillUpdate')
      }
      componentWillReceiveProps() {
        console.log('componentWillReceiveProps')
      }
      render() {
        return (
          <div />
        )
      }
    }
    
    export default class StrictModeExample extends Component {
      render() {
        return (
          <StrictMode>
            <Child />
          </StrictMode>
        )
      }
    }
    

    由于在StrictMode内使用了三个即将废弃的API,打开控制台 ,可看到如下错误提醒:

    控制台报错信息
    注释:严格模式检查只在开发模式下运行,不会与生产模式冲突。
    1. createRef (v16.3)
      (1) 老版本ref使用方式
      ① 字符串形式: <input ref="input" />
      ② 回调函数形式:<input ref={input => (this.input = input)} />
      (2) 字符串形式缺点
      ① 需要内部追踪 refthis 取值,会使 React 稍稍变慢。
      ② 有时候 this 与你想象的并不一致。
    import React from 'react'
    
    class Children extends React.Component {
      componentDidMount() {
        // <h1></h1>
        console.log('children ref', this.refs.titleRef)
      }
      render() {
        return (
          <div>
            {this.props.renderTitle()}
          </div>
        )
      }
    }
    
    class Parent extends React.Component {
      // 放入子组件渲染
      renderTitle = () => (
        <h1 ref='titleRef'>{this.props.title}</h1>
      )
    
      componentDidMount() {
        // undefined
        console.log('parent ref:', this.refs.titleRef)
      }
      render() {
        return (
          <Children renderTitle={this.renderTitle}></Children>
        )
      }
    }
    
    export default Parent
    
    
    1. 调用setState更新状态时,若之后的状态依赖于之前的状态,推荐使用传入函数形式。
      语法:setState((prevState, props) => stateChange, [callback])
      例如,假设我们想通过props.step在状态中增加一个值:
    this.setState((prevState, props) => {
      return {counter: prevState.counter + props.step};
    });
    
    1. 错误边界
      (1) 错误边界用于捕获其子组件树 JavaScript 异常,记录错误并展示一个回退的 UIReact 组件,而不是整个组件树异常导致页面空白。
      (2) 错误边界在渲染期间、生命周期方法内、以及整个组件树构造函数内捕获错误。
      (3) 组件如果定义了static getDerivedStateFromError()componentDidCatch()中的任意一个或两个生命周期方法 。当其子组件抛出错误时,可使用static getDerivedStateFromError()更新state,可使用componentDidCatch()记录错误信息。
    class ErrorBoundary extends React.Component {
      constructor(props) {
        super(props);
        this.state = { hasError: false };
      }
    
      static getDerivedStateFromError(error) {
        // Update state so the next render will show the fallback UI.
        return { hasError: true };
      }
    
      componentDidCatch(error, info) {
        // You can also log the error to an error reporting service
        logErrorToMyService(error, info);
      }
    
      render() {
        if (this.state.hasError) {
          // You can render any custom fallback UI
          return <h1>Something went wrong.</h1>;
        }
    
        return this.props.children; 
      }
    }
    

    而后你可以像一个普通的组件一样使用:

    <ErrorBoundary>
      <MyWidget />
    </ErrorBoundary>
    

    注释:getDerivedStateFromErrorcomponentDidCatch方法使用一个即可捕获子组件树错误。

    1. Portal
      (1) Portals 提供了一种很好的将子节点渲染到父组件以外的 DOM 节点的方式。
      (2) 通过 Portals 进行事件冒泡
      尽管 portal 可以被放置在DOM 树的任何地方,但在其他方面其行为和普通的 React 子节点行为一致。一个从 portal 内部会触发的事件会一直冒泡至包含 React 树 的祖先。
    import React from 'react';
    import ReactDOM from 'react-dom';
    
    class Modal extends React.Component {
      render() {
        return this.props.clicks % 2 === 1
          ? this.props.children
          : ReactDOM.createPortal(
            this.props.children,
            document.getElementById('modal'),
          );
      }
    }
    
    class Child extends React.Component {
      componentWillUnmount() {
        debugger;
      }
    
      render() {
        return (
          <button>Click</button>
        );
      }
    }
    
    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {clicks: 0};
        this.handleClick = this.handleClick.bind(this);
      }
    
      handleClick() {
        this.setState(prevState => ({
          clicks: prevState.clicks + 1
        }));
      }
    
      render() {
        return (
          <div onClick={this.handleClick}>
            <p>Number of clicks: {this.state.clicks}</p>
            <Modal clicks={this.state.clicks}>
              <Child />
            </Modal>
          </div>
        );
      }
    }
    
    export default Parent
    

    注释:当组件由portal渲染方式切换为普通渲染方式,会导致该组件被卸载之后重新渲染。组件放大功能如果通过portal方式实现,放大前的状态(滚动位置、焦点位置等)无法保持。

    3. React生态圈

    3.1 使用Charles实现mock数据

    1. 桌面创建todolist.json文件
    ➜  ~ cd Desktop
    ➜  Desktop touch todolist.json
    
    1. 编辑todolist.json文件
      ["Dell", "Lee", "Imooc"]
    2. 打开Charles代理
      Charles - Proxy - macOS Proxy
    3. 配置Charles本地代理
      Charles - Tools - Map Local Settings - add
      ② 添加请求代理映射
      image.png
    4. 使用axios请求mock数据
      ① 安装axois
    ➜  my-react-app git:(master) ✗ yarn add axios 
    

    注释:也可以使用npm install axios --save安装。
    ② 使用axios请求数据

    import axios from 'axios'
    //省略...
    axios.get('/api/todolist')
      .then(data => {
        console.log(data)
      })
      .catch(err => {
        console.log(err)
      }) 
    

    3.2 react-transition-group

    1. 文档地址
    2. 安装react-transition-group
    ➜  my-react-app git:(master) ✗ yarn add react-transition-group
    
    1. CSSTransition组件的简单使用
    import React, { Component } from 'react';
    import { CSSTransition } from 'react-transition-group'
    import './style.css'
    
    class App extends Component {
    
      constructor(props, context){
        super(props, context)
        this.state = {
          show: true
        }
      }
    
      render() {
        
        return (
          <div>
            <CSSTransition
              in={this.state.show}
              timeout={1000}
              classNames='fade'
              appear={true} //第一次展示时也添加动画效果
              unmountOnExit //出场之后将元素移除
              onEntered={   //入场动画结束钩子函数 
                el => console.log(el) //el表示内部元素
              }  
            >
              <div>Hello</div>
            </CSSTransition>
            <button onClick={this._toggleShow}>Toggle</button>
          </div>
        );
      }
    
      _toggleShow = () => {
        this.setState({
          show: !this.state.show
        })
      }
    }
    
    export default App;
    
    /* 定义class为fade的
    CSSTransition元素第一次展示时动画 */
    .fade-appear {
      opacity: 0;
    }
    
    .fade-appear-active {
      opacity: 1;
      transition: opacity 1s ease-in
    }
    
    /* 定义class为fade的
    CSSTransition元素入场动画 */
    .fade-enter {
      opacity: 0;
    }
    
    .fade-enter-active {
      opacity: 1;
      transition: opacity 1s ease-in
    }
    
    .fade-enter-done {
      opacity: 1;
    }
    
    /* 定义class为fade的
    CSSTransition元素出场动画 */
    .fade-exit {
      opacity: 1;
    }
    
    .fade-exit-active {
      opacity: 0;
      transition: opacity 1s ease-in
    }
    
    .fade-exit-done {
      opacity: 0;
    }
    
    1. CSSTransition组件的其它钩子函数
      onEnter:入场动画执行第一帧时钩子函数
      onEntering:入场动画执行第二帧时钩子函数
      onEntered:入场动画执行最后一帧钩子函数
      onExit:出场动画执行第一帧时钩子函数
      onExiting:出场动画执行第二帧时钩子函数
      onExited:出场动画执行最后一帧钩子函数
    2. Transition是更底层组件,CSSTransition组件是根据Transition组件实现的。

    3.3 Redux

    3.3.1 Redux入门

    1. Redux Devtools调试插件安装
      设置 - 扩展程序 - 打开Chrome网上应用店 - Redux Devtools
    2. 使用Redux实现TodoList

    (1) 安装redux

    ➜  my-react-app git:(master) ✗ yarn add redux
    

    (2) 创建Store

    // src/store/index.js
    import { createStore } from 'redux'
    import reducer from './reducer'
    
    //store是唯一的,只有store能改变state
    const store = createStore(
      reducer,
      //Redux DevTools调试工具
      window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
    )
    
    export default store
    

    (3) 创建actionTypes

    // src/store/actionTypes.js
    export const CHANGE_INPUT_VALUE = 'change_input_value'
    export const ADD_TODO_ITEM = 'add_todo_item'
    export const DELETE_TODO_ITEM = 'delete_todo_item'
    

    (4) 创建actionCreators

    // src/store/actionCreators.js
    import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
    
    export const getInputChangeAction = (value) => ({
      type: CHANGE_INPUT_VALUE, value
    })
    
    export const getAddTodoItemAction = () => ({
      type: ADD_TODO_ITEM
    })
    
    export const getDeleteTodoItemAction = (value) => ({
      type: DELETE_TODO_ITEM, value
    })
    

    (5) 实现reducer

    // src/store/reducer.js
    import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
    
    const defaultState = {
      inputValue: '',
      list: []
    }
    
    //redux是一个纯函数,输入固定则输出固定,且没有副作用
    //redux不应修改旧的state
    export default (state = defaultState, action) => {
      switch(action.type){
        case  CHANGE_INPUT_VALUE:
          return {
            ...state,
            inputValue: action.value
          }  
        case ADD_TODO_ITEM:
          return {
            ...state,
            inputValue: '',
            list: [...state.list, state.inputValue]
          }
        case DELETE_TODO_ITEM:
          return {
            ...state,
            list: state.list.filter(
              (item, index) => index !== action.value
            )
          }
      }
      return state
    }
    

    (6) 实现TodoList组件

    // src/TodoList.js
    import React, { Component } from 'react';
    import { Input, Button, List } from 'antd'
    import store from './store'
    import {getInputChangeAction, getAddTodoItemAction, getDeleteTodoItemAction} from './store/actionCreators'
    
    class TodoList extends Component {
      constructor(props, context) {
        super(props, context)
        this.state = store.getState()
        this._handleStoreChange = this._handleStoreChange.bind(this)
        this._handleInputChange = this._handleInputChange.bind(this)
        this._handleButtonClick = this._handleButtonClick.bind(this)
        //订阅Store变化,执行_handleStoreChange方法
        store.subscribe(this._handleStoreChange)
      }
    
      render() {
        return (
          <div>
            <div style={{display: 'flex'}}>
              <Input 
                placeholder='todo info'
                value = {this.state.inputValue}
                onChange={this._handleInputChange}
              />
              <Button 
                type="primary"
                onClick={this._handleButtonClick}
              >
                提交
              </Button>
            </div>
            <List
              bordered
              dataSource={this.state.list}
              renderItem={(item, index) => (
                  <List.Item onClick={this._handleItemDelete.bind(this, index)}>{item}</List.Item>
                )
              }
            />
          </div>
        )
      }
    
      _handleStoreChange() {
        this.setState(store.getState())
      }
    
      _handleInputChange(e) {
        store.dispatch(getInputChangeAction(e.target.value))
      }
    
      _handleButtonClick() {
        store.dispatch(getAddTodoItemAction())
      }
    
      _handleItemDelete(index) {
        store.dispatch(getDeleteTodoItemAction(index))
      }
    }
    
    export default TodoList;
    
    1. Redux核心API
      (1) createStore: 创建应用唯一的store
      (2) store.dispatch:派发action, 该action会传递给store
      (3) store.getState:获取store中所有数据内容。
      (4) store.subscribe:订阅store改变,绑定回调函数。

    3.3.2 Redux中间件

    1. Redux中间件介绍
      ① 中间件指actionstore“中间”。中间件是对dispatch方法的升级。
      ② 流程图
      image.png
    2. redux-thunk中间件

    ① 安装redux-thunk

     yarn add redux-thunk
    

    ② 创建store时引入redux-thunk中间件

    //src/store/index.js
    import { createStore, applyMiddleware, compose } from 'redux'
    import reducer from './reducer'
    import thunk from 'redux-thunk'
    
    //Redux DevTools调试工具
    const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
      ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
      : compose;
    
    const enhancer = composeEnhancers(
      applyMiddleware(thunk)
    );
    
    const store = createStore(reducer, enhancer)
    
    export default store
    

    ③ 定义action

    //src/store/actionCreators.js
    export const getInitListAction = (data) => ({
      type: INIT_LIST_ACTION,
      data
    })
    
    export const getInitList = () => {
      return (dispatch) => {
        axios.get('/list.json').then((res) => {
          dispatch(getInitListAction(res.data))
        })
      }
    }
    

    ④ 分发action

      //src/components/TodoList/TodoList.js
      componentDidMount() {
        store.dispatch(getInitList());
      }
    

    redux-thunk使用总结
    使用redux-thunk后,action可以不是一个对象,而是一个方法。 dispatch方法类型的action,会调用该方法,并传入dispatch实参。
    在函数内部可以通过该实参dispatch其他action动作。
    总结:redux-thunk中间件将异步获取数据代码抽离到aciton中。

    1. redux-saga中间件

    ① 安装redux-saga

    yarn add redux-saga
    

    ② 创建store时引入redux-thunk中间件

    //src/store/index.js
    import { createStore, applyMiddleware, compose } from 'redux'
    import reducer from './reducer'
    import createSagaMiddleware from 'redux-saga'
    import todoSagas from './sagas'
    
    const sagaMiddleware = createSagaMiddleware()
    
    //Redux DevTools调试工具
    const composeEnhancers = typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
      ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
      : compose;
    
    const enhancer = composeEnhancers(applyMiddleware(sagaMiddleware));
    
    
    //store是唯一的,只有store能改变state
    const store = createStore(reducer, enhancer)
    
    sagaMiddleware.run(todoSagas)
    
    export default store
    

    ③ 定义sagas

    //src/store/sagas.js
    import { takeEvery, put } from 'redux-saga/effects'
    import { GET_INIT_LIST } from './actionTypes'
    import axios from "axios"
    import {getInitListAction} from "./actionCreators"
    
    function *getInitList() {
      try {
        const res = yield axios.get('/list.json')
        const action = getInitListAction(res.data)
        yield put(action)
      } catch (e) {
        console.log('list.json 网络请求失败!')
      }
    }
    
    //generator函数
    function* mySaga() {
      yield takeEvery(GET_INIT_LIST, getInitList);
    }
    
    export default mySaga;
    

    ④ 分发action

    //src/components/TodoList/TodoList.js
    componentDidMount() {
      store.dispatch(getInitList())
    }
    

    redux-saga使用总结
    使用 redux-saga后,action可以不被reducer处理,而是被saga处理。
    saga函数内部可以通过put方法dispatch其它action动作。
    总结:redux-saga中间件将异步获取数据代码抽离到saga中。

    3.3.3 react-redux介绍

    1. 使用react-redux实现TodoList

    (1) 安装react-redux

    ➜  my-react-app git:(master) ✗ yarn add react-redux
    

    (2) 创建Store

    // src/store/index.js
    import  { createStore } from 'redux'
    import reducer from './reducer'
    
    const store = createStore(reducer)
    
    export default store
    

    (3) 使用Provider组件包裹根组件

    //src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { Provider } from 'react-redux'
    import TodoList from './components/TodoList/TodoList'
    import store from './store'
    
    ReactDOM.render(
      <Provider store={store}>
        <TodoList/>
      </Provider>,
      document.getElementById('root')
    );
    

    注释:store传入Provider组件,使Provider内部组件都有机会获取store中的state
    (4) 创建actionTypes

    // src/store/actionTypes.js
    export const CHANGE_INPUT_VALUE = 'change_input_value'
    export const ADD_TODO_ITEM = 'add_todo_item'
    export const DELETE_TODO_ITEM = 'delete_todo_item'
    

    (5) 创建actionCreators

    // src/store/actionCreators.js
    import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
    
    export const getInputChangeAction = (value) => ({
      type: CHANGE_INPUT_VALUE, value
    })
    
    export const getAddTodoItemAction = () => ({
      type: ADD_TODO_ITEM
    })
    
    export const getDeleteTodoItemAction = (value) => ({
      type: DELETE_TODO_ITEM, value
    })
    

    (6) 实现reducer

    // src/store/reducer.js
    import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from './actionTypes'
    
    const defaultState = {
      inputValue: '',
      list: []
    }
    
    //redux是一个纯函数,输入固定则输出固定,且没有副作用
    //redux不应修改旧的state
    export default (state = defaultState, action) => {
      switch(action.type){
        case  CHANGE_INPUT_VALUE:
          return {
            ...state,
            inputValue: action.value
          }  
        case ADD_TODO_ITEM:
          return {
            ...state,
            inputValue: '',
            list: [...state.list, state.inputValue]
          }
        case DELETE_TODO_ITEM:
          return {
            ...state,
            list: state.list.filter(
              (item, index) => index !== action.value
            )
          }
      }
      return state
    }
    

    (7) 实现TodoList组件

    // src/TodoList.js
    import React from 'react'
    import { Input, Button, List } from 'antd'
    import { connect } from 'react-redux'
    import { getInputChangeAction, getAddTodoItemAction, getDeleteTodoItemAction } from '../../store/actionCreators'
    
    const TodoList = (props) => {
      const {inputValue, handleInputChange, addListItem, list, handleItemDelete} = props
      return (
        <div>
          <di>
            <Input value={inputValue} onChange={handleInputChange}/>
            <Button type="primary" onClick={addListItem}>
              提交
            </Button>
          </di>
          <List bordered dataSource={list} renderItem={(item, index) => (
              <List.Item onClick={() => {handleItemDelete(index)}}>
                {item}
              </List.Item>
            )}
          />
        </div>
      )
    }
    
    const mapStateToProps = (state) => {
      return {
        inputValue: state.inputValue,
        list: state.list
      }
    }
    
    const mapDispatchToProps = (dispatch) => {
      return {
        handleInputChange(e) {
          dispatch(getInputChangeAction(e.target.value))
        },
        handleItemDelete(index) {
          dispatch(getDeleteTodoItemAction(index))
        },
        addListItem() {
          dispatch(getAddTodoItemAction())
        }
      }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(TodoList);
    

    注释:TodoList为一个UI(纯)组件,connect(mapStateToProps, mapDispatchToProps)(TodoList)为一个容器组件。业务逻辑被抽离到mapStateToPropsmapDispatchToProps方法中。

    1. react-reduxredux用法差异
      Provider组件。
      connect方法。
    2. reduxcombineReducers方法的使用
      ① 创建TodoList对应的reducer文件
    // ./src/components/TodoList/store/reducer.js
    import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from '../../../store/actionTypes'
    
    const defaultState = {
      inputValue: '',
      list: []
    }
    
    //redux是一个纯函数,输入固定则输出固定,且没有副作用
    //redux不能修改旧的state
    export default (state = defaultState, action) => {
      switch(action.type){
        case  CHANGE_INPUT_VALUE:
          return {
            ...state,
            inputValue: action.value
          }
        case ADD_TODO_ITEM:
          return {
            ...state,
            inputValue: '',
            list: [...state.list, state.inputValue]
          }
        case DELETE_TODO_ITEM:
          return {
            ...state,
            list: state.list.filter(
              (item, index) => index !== action.value
            )
          }
      }
      return state
    }
    

    ② 使用combineReducers

    // src/store/reducer.js
    import todoListReducer from '../components/TodoList/store/reducer'
    import { combineReducers } from 'redux'
    
    export default combineReducers({
      todoList: todoListReducer
    })
    

    ③ 修改TodoList组件

    // src/components/TodoList/TodoList.js
    //......
    const mapStateToProps = (state) => {
      return {
        inputValue: state.todoList.inputValue,
        list: state.todoList.list
      }
    }
    //......
    

    3.4 styled-components介绍

    1. js文件中引入css文件的弊端
      引入的css文件在整个html中生效。
    2. 安装styled-components
    ➜  my-react-app git:(master) ✗ yarn add styled-components
    
    1. 全局样式

    ① 创建全局样式组件

    //src/style.js
    import { createGlobalStyle } from 'styled-components'
    
    export const Globalstyle = createGlobalStyle`
      body {
        margin: 0;
        padding: 0;
      }
    `
    

    ② 使用全局样式组件

    import { Globalstyle } from './style.js'
    
    ReactDOM.render(
      <Provider store={store}>
        <TodoList/>
        <Globalstyle />
      </Provider>,
      document.getElementById('root')
    );
    
    1. 创建带样式的元素

    ① 创建组件

    //src/components/TodoList/style.js
    import styled from 'styled-components'
    import icon from '../../statics/weixin.jpg'
    
    export const HeaderWrapper  = styled.div.attrs({
      //属性
    })`
      height: 56px;
      background: red;
      background: url(${icon});
      &.right {
        float: right
      }
    `
    

    注释:&.right表示classrightHeaderWrapper组件的样式。
    ② 使用组件

    import { HeaderWraper } from './style'
    const TodoList = (props) => {
      return (
        <HeaderWraper>
         //...
        </HeaderWraper>
      )
    }
    

    注释:该组件是一个有样式的div元素包裹层。

    3.5 Iconfont阿里云图标

    1. 进入阿里云图标网站
    2. 创建项目并添加图标
    3. 下载Unicode至本地并解压
    4. 拷贝文件
      iconfont.cssiconfont.eoticonfont.svgiconfont.ttficonfont.woff文件拷贝到项目中。项目路径如下:
      ./src/statics/iconfonts/iconfont.XXX
    5. 使用styled-components导入CSS文件
      ① 重命名iconfont.cssiconfont.js
      ② 编辑iconfont.js文件
    import { createGlobalStyle } from 'styled-components'
    
    export const IconFontGlobalComponent = createGlobalStyle`
    @font-face {font-family: "iconfont";
      src: url('./iconfont.eot?t=1550907977882'); /* IE9 */
      src: url('./iconfont.eot?t=1550907977882#iefix') format('embedded-opentype'), /* IE6-IE8 */
      url('data:application/x-font-woff2;charset=utf-8;base64,dfA2nmlc/BurCg==') format('woff2'),
      url('./iconfont.woff?t=1550907977882') format('woff'),
      url('./iconfont.ttf?t=1550907977882') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
      url('./iconfont.svg?t=1550907977882#iconfont') format('svg'); /* iOS 4.1- */
    }
    
    .iconfont {
      font-family: "iconfont" !important;
      font-size: 16px;
      font-style: normal;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
    }
    
    .iconstar_blue:before {
      content: "\e625";
    }
    `
    
    1. 引入全局组件
    import { IconFontGlobalComponent } from './statics/iconfonts/iconfont'
    
    ReactDOM.render(
      <Provider store={store}>
        <TodoList/>
        <IconFontGlobalComponent />
      </Provider>,
      document.getElementById('root')
    );
    
    1. 使用字体图标
     <span className="iconfont">&#xe625;</span>
    

    3.6 Immutable的用法

    3.6.1 immutable使用

    1. 安装immutable.js
    ➜  my-react-app git:(master) ✗ yarn add immutable
    
    1. 改写reducer.js
    // ./src/components/TodoList/store/reducer.js
    import { CHANGE_INPUT_VALUE, ADD_TODO_ITEM, DELETE_TODO_ITEM } from '../../../store/actionTypes'
    import { fromJS } from 'immutable'
    
    
    const defaultState = fromJS({
      inputValue: '',
      list: []
    })
    
    export default (state = defaultState, action) => {
      switch(action.type){
        case  CHANGE_INPUT_VALUE:
          return state
            .set('inputValue', action.value)
        case ADD_TODO_ITEM:
          return state
            .set('list', state.get('list').push(state.get('inputValue')))
            .set('inputValue', '')
        case DELETE_TODO_ITEM:
          return state
            .set('list', state.get('list').filter((item, index) => index !== action.value))
      }
      return state
    }
    

    注意:stateimmutable对象,其内部list值也为immutable对象。为保持数据类型统一,当action类型为ADD_TODO_ITEMDELETE_TODO_ITEM时,需要设置list值仍为immutable对象。

    1. 改写TodoList组件
    // src/components/TodoList/TodoList.js
    //......
    const mapStateToProps = (state) => {
      return {
        inputValue: state.todoList.get('inputValue'),
        list: state.todoList.get('list')
      }
    }
    //......
    

    3.6.2 redux-immutable使用

    1. 安装redux-immutable
    ➜  my-react-app git:(master) ✗ yarn add redux-immutable
    
    1. 改写reducer
    //src/store/reducer.js
    import todoListReducer from '../components/TodoList/store/reducer'
    import { combineReducers } from 'redux-immutable'
    
    export default combineReducers({
      todoList: todoListReducer
    })
    

    注释:combineReducersredux-immutable中引入,使该函数生成immutable对象。

    1. 改写TodoList组件
    // src/components/TodoList/TodoList.js
    //......
    const mapStateToProps = (state) => {
      return {
        inputValue: state.getIn(['todoList', 'inputValue']),
        list: state.getIn(['todoList','list'])
      }
    }
    //......
    

    参考资料

    相关文章

      网友评论

          本文标题:React学习与进阶(更新中)

          本文链接:https://www.haomeiwen.com/subject/orknhqtx.html