zoukankan      html  css  js  c++  java
  • react系列

    3.react-router 和react-router-dom的区别  https://blog.csdn.net/sinat_17775997/article/details/69218382

    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-thunk

    redux-thunk实现了相关异步流程内聚到redux的流程中,实现middleware的功能,也便于项目的开发与维护,避免冗余代码。而实现的方式便是改写redux中的dispatch API,使其可以除PlainObject外,接受一个函数作为参数。

    //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>组件的作用,以及如何实现异步加载reducer

    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-redux

    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. 高阶组件

    //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. withRouter

    目的就是让被修饰的组件可以从属性中获取history,location,match,
    路由组件可以直接获取这些属性,而非路由组件就必须通过withRouter修饰后才能获取这些属性了,
    比如 <Route path='/' component={App}/>

    App组件就可以直接获取路由中这些属性了,但是,如果App组件中如果有一个子组件Foo,那么Foo就不能直接获取路由中的属性了,必须通过withRouter修饰后才能获取到。

    //用于js实现路由跳转
    this.props.history.push('/chat)

     11. Redux DevTools 扩展的使用说明

    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;
      }
    }

    https://juejin.im/post/5c5c33aef265da2d8d69b639#heading-3

  • 相关阅读:
    宿主机无法访问CentOS7上Jenkins服务的解决办法
    415. Add Strings
    367. Valid Perfect Square
    326. Power of Three
    258. Add Digits
    231. Power of Two
    204. Count Primes
    202. Happy Number
    172. Factorial Trailing Zeroes
    171. Excel Sheet Column Number
  • 原文地址:https://www.cnblogs.com/luguiqing/p/10211981.html
Copyright © 2011-2022 走看看