React Internals

HOC (Higher-Order Components)

render function must return:

  • a virtual element

  • an array of virtual elements with the prop key

However, the array is not readable, the better way is to wrap virtual elements in a virtual elements

const Aux = props => props.children;

export default Aux;
<Aux>
    <!-- some virtual elements -->
</Aux>

Aux is HOC. React provides a same built-in HOC, Fragment.

HOC does two things:

  1. wrap an array of virtual elements
  2. add extra information to an array of virtual elements

HOC can be created in 2 ways:

  • as a Component

    WithClass.js (Upper case indicates it's a component)

    const WithClass = props => (
        <div className={props.classes}>{props.children}</div>
    );
    
    export default WithClass;
    

    App.js

      render () {
        return (
            <WithClass classes="App">
            </WithClass>
        );
    }
    
  • as a closure (a function enclosed with lexical environment))

    withClass.js (L ower case indicates it's a function)

    const withClass = (WrappedComponent, className) => {
        return props => (
            <div className={className}>
                <WrappedComponent {...props}/>
            </div>
        );
    }
    
    export default withClass;
    

    App.js

    render () {
        return (
            <Fragment>
            </Fragment>
        );
    }
    
    export default withClass(App, 'App');
    

    To pass props: <WrappedComponent {...props}/>

PropTypes

To define the type of the props

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

Refs

Refs provide a way to access DOM nodes or React elements created in the render method.

Avoid using refs for anything that can be done declaratively.

There are 3 ways:

  • function: pass function to rel

    class MyInput extends Component {
        componentDidMount() {
            this.inputEl.focus();
        }
        render() {
            <input rel={(el) => this.inputEl = el}>
        }
    }
    
  • React.createRef(): create an elementRef and pass it to ref

    class MyInput extends Component {
        constructor(props) {
            super(props);
            this.inputElementRef = React.createRef();
        }
        componentDidMount() { 
            this.inputElementRef.current.focus();
        }
        render() {
            <input rel={this.inputElementRef}>
        }
    }
    
  • useRef: React hook

    const MyInput = props => {
        const inputElementRef = useRef(null);
        // Similar to componentDidMount and componentDidUpdate:
        useEffect(() => {
            inputElementRef.current.focus();
        })
        
        return <input rel={inputElementRef}>
    }
    

Context

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Sometimes, the same data needs to be accessible by many components in the tree, and at different nesting levels. Context lets you “broadcast” such data, and changes to it, to all components below.

  1. create context (AuthContext.js)

    const AuthContext = React.createContext({
        authenticated: false,
        login: () => {}
    })
    
  2. provide context (ParentClass.js)

    <AuthContext.Provider value={
        {
            authenticated: this.authenticated
            login: this.loginHandler
        }
    }>
        <!-- the components consume the AuthContext -->
    </AuthContext.Provider>
    
  3. consume context (MyClass.js)

    <AuthContext.Consumer>
        {context => context.authenticated 
            ? <p>logged in</p> 
            : <p>not logged in</p> 
        }
    </AuthContext.Consumer>
    

    or, another way to consume context is using contextType

    class MyClass extends React.Component {
        // subscribe the context
        static contextType = AuthContext;
        // This lets you consume the nearest current value of that Context type using this.context. 
        // You can reference this in any of the lifecycle methods including the render function.
        render() {
            let authenticated = this.context.authenticated;
            /* render something based on the value */
        }
    }
    

    However, you can only subscribe to a single context using this API.

    React hooks also can access the context.

    const authContext = useContext(AuthContext);
    console.log(authContext.authenticated);