This article from a small point, thinking about, a look at a thousand lines under the hook source code. Then came the React Ban, just like Harry Potter’s Apparition Charm.

Are you okay? It takes getting used to. “– Albus Dumbledore

Story

1. Origin

The following components, a, B, C judgment condition to write three, how to write?

export const Origin: React.FC = () = > {
  const a = true;
  const b = true;
  const c = true;
  return (
    <div>
      {(a || b || c) && (
        <div>
          <h5>title</h5>
          {a && <div>a</div>}
          {b && <div>b</div>}
          {c && <div>c</div>}
        </div>
      )}
    </div>
  );
};

Copy the code

2. A. children B. children C. children

Check ReactChildNode as follows, if all are empty, return NULL. Don’t need to write it again all the conditions a | b | | | c.

Look before you leap, it looks a lot more complicated than the original code

const Wrapper: React.FC = ({ children }) = > {
  const children = (props as any)? .childrenconsole.info('children', children)
  if (
    !children ||
    (Array.isArray(children) && children.every((item) = >! item)) ) {return null
  }
  return (
    <div>
      <h5>title</h5>
      {children}
    </div>)}export const V1: React.FC = () = > {
  const a = true
  const b = false
  const c = false

  return (
    <Wrapper>
      {a && <div>a</div>}
      {b && <div>b</div>}
      {c && <div>c</div>}
    </Wrapper>)}Copy the code

3. When DO I encounter an empty component?

I think I can still judge.

const Null: React.FC = () = > null

export const V2: React.FC = () = > {
  return (
    <Wrapper>
        <Null />
        <Null />
    </Wrapper>)}Copy the code

The React Node is a function. I’ll run it

Console. info(
) sees {… type: fn Null(), props: {} … }

As a result,


const Wrapper: React.FC = ({ children }) = > {
  const children = (props as any)? .childrenconsole.info('children', children)
  if (
    !children ||
    (Array.isArray(children) && children.every((item) = > {
        if(! item) {return true
        }
        if(typeofitem? .type ==='function') {
            return! item.type(item.props) }return false{})))return null
  }
  return (
    <div>
      <h5>title</h5>
      {children}
    </div>)}Copy the code

node can be ignored.

5 although can, hidden excellent huge, such as too<Null />There are hooks

constNull: React.FC<{ hi? :string} > =({ hi = '_' }) = > {
  useEffect(() = > {
    console.info('null <--', hi)
    return () = > {
      console.info('null -->', hi)
    }
  }, [])
  return null
}
Copy the code

[Figure 1. When I run, children are empty, can be filtered out, but run once]

[Figure 2. Run twice when I run, children not all empty]

6. Can I get a function to eliminate side effects? If I can, I’ll execute it ahead of time.

What is useEffect first? React/SRC/reacthooks.js# L95

@flow
export function useEffect(
  create: () => (() => void) | void,
  deps: Array<mixed> | void | null,
): void {
  const dispatcher = resolveDispatcher()
  return dispatcher.useEffect(create, deps)
}
Copy the code

What is a dispatcher? Will the results of useEffect calls exist on it? 🤔 ️

import ReactCurrentDispatcher from './ReactCurrentDispatcher';

function resolveDispatcher() {
  return ReactCurrentDispatcher.current;
}
--- ./ReactCurrentDispatcher.js
const ReactCurrentDispatcher = {
  current: (null: null | Dispatcher),
};

export default ReactCurrentDispatcher;
Copy the code

Gee, there’s an Export default, that’s fine

[Figure 1. I use the editor to type react.reactcu… and it says nothing.]

[Figure 2, I clicked node_modules/@types/react/index.d.ts full text search 🔍 ReactCurrentDispatcher, nothing]

[figure 3, I opened the node_modules/react/CJS/react. Development. The js, 🔍 search here!!]

var ReactSharedInternals = {
  ReactCurrentDispatcher: ReactCurrentDispatcher,
  ...
};

exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = ReactSharedInternals;
Copy the code

Using this API will get you fired 🦑, 😯! Also, I can’t seem to get the return content

6.1 But, seeing.current, I had a second thought

I replace dispatcher.current with dispatcher.current during synchronous method execution. so

export const apparate = function (exec: () => any) {
  const x = (React as any).__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
  const origin = x.ReactCurrentDispatcher.current
  x.ReactCurrentDispatcher.current = new Proxy({}, {get: () = > () = > undefined,},)const result = exec()
  x.ReactCurrentDispatcher.current = origin
  return result
}

export const Wrapper: React.FC<T> = (props) = > {
  const children = (props as any)? .childrenconsole.info('children', children)
  if (
    !children ||
    (Array.isArray(children) &&
      children.every((item) = > {
        if(! item) {return true
        }
        if (typeofitem? .type ==='function') {
          try {
            return! apparate(() = > item.type(item.props))
          } catch (error) {
            console.warn(' execute error')
            return false}}return false{})))return null
  }
  return (
    <div>
      <h5>title</h5>
      {children}
    </div>)}Copy the code

👀 see, when executing function Component function, apparate in place.

[Figure 1. I execute code, children empty, not execute]

[Figure 2. I execute code, children are not empty, execute once]

Okay, better than expected, better than the planned active elimination of side effects

After the

Play

Encapsulate and play

My Style:

/** Deletrius, if the child element is empty, it disappears */
export function selfOffHoc<T> (Wrapper: React.FC<T>) {
  const SelfOffWrapper: React.FC<T> = (props) = > {
    const children = (props as any)? .childrenif (
      [].concat(children).every((item: any) = > {
        if(! item) {return true
        }
        if (typeofitem? .type ==='function') {
          try {
            return! apparate(() = > item.type(item.props))
          } catch (error) {
            console.warn(' execute error')
            return false}}return false{}))return null
    }
    return <Wrapper {. props} >{children}</Wrapper>
  }
  return SelfOffWrapper
}
Copy the code

Usage:

// const ExtraWrapper: React.FC<{ loading? : boolean }> = ...
constExtraWrapper = selfOffHoc<{ loading? :boolean} > (({ children }) = > (
  <View style={{ marginRight: '0px' }}>
    <View>Below:</View>
    <View>{children}</View>
  </View>
))
// Above, there seems to be no lock in the conventional way

export default function () {
  return (
    <ExtraWrapper>
      {null && <a>1</a>}
      {false && <b>2</b>}
      {0 && <code>3</code>}
      <Null />
    </ExtraWrapper>)}Copy the code

Rachel:

  • This method can be used to reduce the judgment condition of redundant one time for purely demonstrative components and components without side effects
  • If the useEffect changes its state, it is not applicable

    Bonus tip — Don’t do it in… Challenge it on the shoulder of _BE_FIRED and do itAsync Apparition– will probably write a whole set of anti-React hooks, maybe pass by@vue/reactivity.

“It does not to dwell on dreams, and forget to live.” — Dumbledore

You can’t live in dreams, Harry. Don’t rely on them and forget to live. — Dumbledore