0

I have the following code:

export function App() {
  return (
    <div>
      <EditText tags={[[["b",28,32]],[["br",40,40]],[["i",43,88],["b",56,64],["b",74,84]],[["b",102,105]]]}/> 
    </div>
  );
}

export const EditText = ({ tags }) => (
    <>
        {
        tags.map((section, id) => {
            console.log("id",id);
            if (section[0][0] === "i") { 
                if (id!==0) {
                    console.log("entering recursion");
                    return <EditText key={id} tags={section} />
                }
            }
            return("");
        })
        }
    </>
);

To make it more visually, the array looks like this:

[
  [["b",28,32]],
  [["br",40,40]],
  [
    ["i",43,88],["b",56,64],["b",74,84]
  ],
  [["b",102,105]]
]

I would expect this algorithm to print in the console the ids like this:

0 1 2 0 1 2 3

because the recursion is called after the third sequence in the map. But instead, it's printing this:

0 1 2 3 0 1 2

It's ending the first map and only at the end of it he remembers to call the recursion.

Can anyone explain me why this is happening?

8
  • you only have one map, so it prints only 0 1 2 3 Commented Nov 24, 2020 at 10:33
  • I have a recursion in the middle, so it gets back to the array, it prints what I wrote in the post. Commented Nov 24, 2020 at 10:52
  • 1
    @Yoshi that's not the correct advise. The execution order is reliable, it's just console.log in render function is not the right way to go. Commented Nov 24, 2020 at 12:25
  • 1
    @SomeoneSpecial I would still disagree. Currently (though bound to change) render might be/act synchronous (ref: overreacted.io/react-as-a-ui-runtime/#consistency) but the the execution order is still breadth first instead of depth first in this case because of lazy evaluation (ref: overreacted.io/react-as-a-ui-runtime/#lazy-evaluation). The child components (recursive call) act as functions which are not immediately called, which explains the observed behavior. Commented Nov 24, 2020 at 13:57
  • 1
    You are right about the sequence of execution. HOWEVER, when you are writing react components, if you follow the rules, you can be assured of execution order, and see the component rendered at the place you want it to be. The functions are called (hence the console.log showing up), then the html codes are generated, then it's rendered. Which explains why the order of rendered component and the console.log call are of different sequences. Commented Nov 24, 2020 at 14:49

2 Answers 2

1

I still think there are some misconceptions at play here, so to answer your concrete question:

It's ending the first map and only at the end of it he remembers to call the recursion.

Can anyone explain me why this is happening?

Your assumptions are correct. The loop (tags.map(...)) runs to completion before the recursive call is made (which explains the console.log output).

This happens when you map some values to new components (like: return <EditText key={id} tags={section} />), these new component (acting as a functions) will not be called/executed immediately at this point, but only when react encounters them while rendering the newly created array (the return value of .map).

This differs in behavior from the plain javascript example shown in Someone Special's answer. Where the recursive call is made immediately (editText(section)).

Sign up to request clarification or add additional context in comments.

Comments

0

Something wrong with your real codes? It's working as expected.

tags = [
  [
    ["b", 28, 32]
  ],
  [
    ["br", 40, 40]
  ],
  [
    ["i", 43, 88],
    ["b", 56, 64],
    ["b", 74, 84]
  ],
  [
    ["b", 102, 105]
  ]
]

const editText = (tags) => {
        tags.map((section, id) => {
            console.log("section_id",id);
            if (section[0][0] === "i") { 
                if (id!==0) {
                    console.log("entering recursion");
                    editText(section)
                }
            }
        })


}

editText(tags)

Update: It's multiple nested array, I threw in some necessary checks. This is not perfect code but it shows you render 0, 1, recurisve2, 3 to screen, despite the outcome of the console.log

https://codesandbox.io/s/naughty-lehmann-t5vfr?file=/src/App.js:363-922

export const EditText = ({ tags }) => {
  console.log("tags", tags);
  return (
    <>
      {tags.map((section, id) => {
        console.log("id", id);
       
        if ( Array.isArray(section[0])){
          if (section[0][0] === "i") {
          if (id !== 0) {
            console.log("entering recursion");
            return <div><EditText key={id} tags={section[0]} />recursive{id}</div>
          } 
        } 
      }
      if (Array.isArray(section)) {
        return <div key={Math.random()}>{id}</div>;
      }
        
      })}
    </>
  );
};

3 Comments

It works in plain Javascript but in JSX it apparently doesn´t (only writing this again here so others know the problem is not solved yet).
Updated to show react is working as it should, and sandbox shows console.log is not reliable in this case. multi nested array is more painful to work with then arrays of objects since it's not idiomatic.
This is a good lesson for us to show we cannot rely on console.log to show the execution order in the rendering function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.