react系列

3.react-router 和react-router-dom的區別  https://blog.csdn.net/sinat_17775997/article/details/69218382javascript

4.Route關聯component有多種形式(render、component、children) children定義形式與render和component的不一樣在於,children的執行與match無關,即便match爲null,children函數也是會執行的
(1) component 會重複加載,每次從新調用React.createElement(component, props)
(2) render 內聯渲染,不會從新加載
5.插入html元素

 6.redux-thunkcss

redux-thunk實現了相關異步流程內聚到redux的流程中,實現middleware的功能,也便於項目的開發與維護,避免冗餘代碼。而實現的方式即是改寫redux中的dispatch API,使其能夠除PlainObject外,接受一個函數做爲參數。html

//action.js
const custom = (data) => {
  type: 'SET_CUSTOM_DATA',
  data: data    
}

dispatch(custom({}))

//redux-thunk

const custom = (data) => {
  return async (dispatch, getState) => {
     //異步操做
     dispatch({
        type: 'SET_CUSTOM_DATA',
        data: data 
    })
  }
    
}
dispatch(custom({}))

7. react-redux中<Provider>組件的做用,以及如何實現異步加載reducervue

import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)
//App的全部子組件就默認均可以拿到state
//它的原理是React組件的context屬性

class Provider extends Component {
  //store放在了上下文對象context上面。而後,子組件就能夠從context拿到store
  getChildContext() {
    return {
      store: this.props.store
    };
  }
  render() {
    return this.props.children;
  }
}

Provider.childContextTypes = {
  store: React.PropTypes.object
}        


//子組件調用
class VisibleTodoList extends Component {
  componentDidMount() {
    const { store } = this.context;
    this.unsubscribe = store.subscribe(() =>
      this.forceUpdate()
    );
  }

  render() {
    const props = this.props;
    const { store } = this.context;
    const state = store.getState();
    // ...
  }
}

VisibleTodoList.contextTypes = {
  store: React.PropTypes.object
}

//react組件的context屬性應用,將store嵌入,經過this.context.store獲取
//injectReducer在項目中異步加載reducers,具體的reducer能夠在生命週期函數中加載進去

 8.  react-reduxjava

export default connect(function (state, props){
  /*return {
    ...state,
    name: [state.name, props.name]
  };*/
  // state, props若是同名,能夠經過這個函數決定是獲取哪一個
  return state.user;
}, {
  setName(name){
    return {
      type: SET_NAME,
      name
    };
  },
  addAge(n){
    return {
      type: ADD_AGE,
      n
    }
  }
})(App);

 9. 高階組件node

//withHoc.js
import React, { Component } from 'react';

export default (params) => (WrappedComponent) => {
    return class From extends Component {
        //方便使用react-devtool調試時顯示不一樣組件名
        static displayName = `From(${WrappedComponent.name || WrappedComponent.displayName})`
        render() {
          return (
            <div className="withHoc">
              <div>{params}</div>
              <WrappedComponent {...this.props} {...this.state}></WrappedComponent>
            </div>
          );
        }
      }
}

//a.js
import React, { Component } from 'react';
import withHoc from './withHoc.js';

class A extends Component {
  render() {
    return (
      <div className="A">
        <div>A component</div>
      </div>
    );
  }
}

export default withHoc('a')(A);

 10. withRouterreact

目的就是讓被修飾的組件能夠從屬性中獲取history,location,match,
路由組件能夠直接獲取這些屬性,而非路由組件就必須經過withRouter修飾後才能獲取這些屬性了,
好比 <Route path='/' component={App}/>webpack

App組件就能夠直接獲取路由中這些屬性了,可是,若是App組件中若是有一個子組件Foo,那麼Foo就不能直接獲取路由中的屬性了,必須經過withRouter修飾後才能獲取到。git

//用於js實現路由跳轉
this.props.history.push('/chat)

 11. Redux DevTools 擴展的使用說明github

if (process.env.NODE_ENV === 'development') {
  const devToolsExtension = window.__REDUX_DEVTOOLS_EXTENSION__

  if (typeof devToolsExtension === 'function') {
    enhancers.push(devToolsExtension())
  }
}

const composedEnhancers = compose(
  applyMiddleware(...middlewares),
  ...enhancers
)

//或者
const enhancers = []
let composeEnhancers = compose

// 在development模式,使用redux-devtools-extension
if (process.env.NODE_ENV === 'development') {
  if (typeof window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ === 'function') {
    composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  }
}
const store = createReduxStore(
  makeRootReducer(),
  initialState,
  composeEnhancers(
    applyMiddleware(...middleware),
    ...enhancers
  )
)

12. 經常使用npm包

connected-react-router
core-js 
reselect // computed
redux-undo // 撤銷,重作
react-loadable //懶加載

13. px2rem

// config-overrides.js
const px2rem=require('postcss-px2rem-exclude')

module.exports = {
  webpack: override(

    // 用js的方式導入antd及其樣式:style爲true表示導入antd.less; 爲false表示不使用js的方式導入antd.less或antd.css;爲'css'表示使用antd.css;
    fixBabelImports("import", {
      libraryName: "antd-mobile", libraryDirectory: "es", style: true  // 爲false或css會致使addLessLoader失效
    }),
    addLessLoader({
      javascriptEnabled: true,
      // modifyVars: { "@primary-color": "#D24545" } // 深紅色
    }),
    addPostcssPlugins([
      px2rem({
        remUnit: 75,

        // 僅排除對antd-mobile的px2rem轉化
        exclude: /node_modules\/antd-mobile/i
      })
    ]),
    disableEsLint() // 取消eslint檢查,加快yarn start速度
  ),
  devServer: overrideDevServer(
    // dev server plugin
    watchAll()
  )
}
// 某一項不想轉爲rem
border: 1px solid #ccc; /*no*/

vue-cli3用的是postcss-plugin-px2rem; 實現原理同樣。重要!! 若是個別地方不想轉化px。能夠簡單的使用大寫的 PX 或 Px

14. withRouter致使組件重複渲染

React Router 4 把Route看成普通的React組件,能夠在任意組件內使用Route;

withRouter 爲非路由組件提供了location,history,match三個參數;可是有時會發現有些接口會重複調用,這個是因爲組件從新渲染的緣由

通過Redux connect後的Home組件,在更新階段,會使用淺比較,可是因爲Route組件致使這個失效

componentWillReceiveProps(nextProps, nextContext) {
    warning(
      !(nextProps.location && !this.props.location),
      '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.'
    )

    warning(
      !(!nextProps.location && this.props.location),
      '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.'
    )

    // 注意這裏,computeMatch每次返回的都是一個新對象,如此一來,每次Route更新,setState都會從新設置一個新的match對象
    this.setState({
      match: this.computeMatch(nextProps, nextContext.router)
    })
  }

  render() {
    const { match } = this.state
    const { children, component, render } = this.props
    const { history, route, staticContext } = this.context.router
    const location = this.props.location || route.location
    // 注意這裏,這是傳遞給Route中的組件的屬性
    const props = { match, location, history, staticContext }

    if (component)
      return match ? React.createElement(component, props) : null

    if (render)
      return match ? render(props) : null

    if (typeof children === 'function')
      return children(props)

    if (children && !isEmptyChildren(children))
      return React.Children.only(children)

    return null
  }

這樣,每次Route更新(componentWillReceiveProps被調用),都將建立一個新的match;

致使Redux的淺比較失敗,進而觸發組件的從新渲染

解決方法:1⃣️. connected-react-router 2⃣️. 直接引用history.js文件

15.input 在IE11,不觸發onchange

<input
  type='text'
  value={this.state.value}
  onCompositionStart={this.handleComposition}
  onCompositionUpdate={this.handleComposition}
  onCompositionEnd={this.handleComposition}
  onChange={this.handleChange}
/>

//前2個事件都在onChange以前觸發,onCompositionEnd是在onChange以後觸發。
//若是直接輸入完成是不會觸發這三個事件的,只有onChange事件。好比直接輸入英文

// ie11下中文輸入法會不觸發onChange,因此也須要setState,不然此時會發現中文輸入進去後輸入框沒有變換

/**
   * 中文輸入法,選詞
   */
  handleComposition = (e) => {
    this.isOnComposition = e.type !== 'compositionend'
    if (!this.isOnComposition) {
      //ie11不觸發onchange致使中文不展現
      this.setState({
        value: e.target.value
      })
      this.handleInputAndSearch(e.target.value)
    }

 16. 若是map的組件爲受控組件,則使用索引並不會產生問題,可是若是爲非受控組件,例如input等,則會因爲複用標籤元素致使value並未更改

17. setState機制https://github.com/sisterAn/blog/issues/26

批處理的緣由,舉例來講,若是咱們在瀏覽器中click處理,都ChildParent調用setState,咱們不想從新渲染Child兩次

補充,這裏輸出 0,0,3,4

componentDidMount() {
      this.setState((prevState, props) => ({
        val: prevState.val + 1
      }))
      console.log(this.state.val)
      this.setState((prevState, props) => ({
        val: prevState.val + 1
      }))
      console.log(this.state.val)
  
      setTimeout(() => {
        this.setState({val: this.state.val + 1});
        console.log(this.state.val);  // 第 3 次 log
  
        this.setState({val: this.state.val + 1});
        console.log(this.state.val);  // 第 4 次 log
      }, 0);

    }
// 0,1 "logbefore", 2 "log",3,4
componentDidMount() {
      this.setState((prevState, props) => ({
        val: prevState.val + 1
      }))
      console.log(this.state.val)
      Promise.resolve().then(() => {
        console.log(this.state.val, 'logbefore');
        this.setState({val: this.state.val + 1});
        console.log(this.state.val, 'log');
      })
  
      setTimeout(() => {
        this.setState({val: this.state.val + 1});
        console.log(this.state.val);  // 第 3 次 log
  
        this.setState({val: this.state.val + 1});
        console.log(this.state.val);  // 第 4 次 log
      }, 0);

    }

(圖片連接來源:https://stackoverflow.com/questions/48563650/does-react-keep-the-order-for-state-updates/48610973#48610973

 

 源碼路徑(v16.9.0)

/react/packages/react-test-renderer/src/ReactShallowRenderer.js

class Updater {
  constructor(renderer) {
    this._renderer = renderer;
    this._callbacks = [];
  }
  //...
  enqueueSetState(publicInstance, partialState, callback, callerName) {
    this._enqueueCallback(callback, publicInstance);
    const currentState = this._renderer._newState || publicInstance.state;

    if (typeof partialState === 'function') {
      partialState = partialState.call(
        publicInstance,
        currentState, //這裏確保每次的state都是當時最新的
        publicInstance.props,
      );
    }

    // Null and undefined are treated as no-ops.
    if (partialState === null || partialState === undefined) {
      return;
    }

    this._renderer._newState = {
      ...currentState,
      ...partialState,
    };

    this._renderer.render(this._renderer._element, this._renderer._context);
  }
}

 /react/src/renderers/shared/stack/reconciler/ReactUpdates.js

function enqueueUpdate(component) {
  ensureInjected();

  // Various parts of our code (such as ReactCompositeComponent's
  // _renderValidatedComponent) assume that calls to render aren't nested;
  // verify that that's the case. (This is called by each top-level update
  // function, like setState, forceUpdate, etc.; creation and
  // destruction of top-level components is guarded in ReactMount.)

  if (!batchingStrategy.isBatchingUpdates) {// 仍是根據isBatchingUpdates
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  dirtyComponents.push(component);
  if (component._updateBatchNumber == null) {
    component._updateBatchNumber = updateBatchNumber + 1;
  }
}

http://www.javashuo.com/article/p-qsugrtyo-md.html

相關文章
相關標籤/搜索