Search code examples
javascriptreactjsfunction-prototypes

React and Javascript: Equivalent for C function prototypes?


Some experimentation I'm doing with React has gotten me the following code:

const BreadNav = props => {
  const initial={
    stack:[
      {
        name:"Home",
        render:React.cloneElement(
          props.children,
          {pushElement:pushElement}
        ),
        state:{}
      }
    ],
  };

  const [state, setState] = React.useState(initial);

  const pushElement = (oldState,elem) => {
    let newStack = JSON.parse(JSON.stringify(state.stack));
    newStack[newStack.length-1].state = oldState;
    newStack.push(elem);
    setState({
      ...state,
      stack:newStack
    });
  }
  
  return(
    state.stack[state.stack.length-1].render
  );
}

React gives me ReferenceError: can't access lexical declaration 'pushElement' before initialization which makes sense for the order the lines are in. If this was C, I would just throw in a function prototype to declare pushElement and define it later, but I haven't seen a javascript answer for this. How do I avoid this trouble?


Solution

  • You can use a function declaration, which is "hoisted", instead of assigning a function value to a const variable:

    function BreadNav(props) {
      const initial = {
        stack: [
          {
            name: "Home",
            render: React.cloneElement(props.children, {pushElement}),
            state: {}
          }
        ],
      };
    
      const [state, setState] = React.useState(initial);
    
      function pushElement(oldState,elem) {
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        let newStack = JSON.parse(JSON.stringify(state.stack));
        newStack[newStack.length-1].state = oldState;
        newStack.push(elem);
        setState({
          ...state,
          stack: newStack
        });
      }
      
      return state.stack[state.stack.length-1].render;
    }
    

    but in this case it would be much more appropriate to define the function before you use it in the initial object literal:

    const BreadNav = props => {
      const pushElement = (oldState, elem) => {
        let newStack = JSON.parse(JSON.stringify(state.stack));
        newStack[newStack.length-1].state = oldState;
        newStack.push(elem);
        setState({
          ...state,
          stack: newStack
        });
      };
      const initial = {
        stack: [
          {
            name: "Home",
            render: React.cloneElement(props.children, {pushElement}),
            state: {}
          }
        ],
      };
    
      const [state, setState] = React.useState(initial);
      
      return state.stack[state.stack.length-1].render;
    }
    

    You can still refer to the state and setState variables declared below, they are in scope, you just must make sure not to call the function before they are initialised.