How React is able to access the variable before initialization? - javascript

function app() {
console.log(myVariable);
}
app();
const myVariable = "hello world";
I understand that the above code snippet produces an error because I am try to access a const variable before initializing it but the code below doesn't give me any error even though I am trying to access myVariable before initialization. why is it so? How am I able to access myVariable even before initializing it ?
import { createRoot } from "react-dom/client";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{console.log(myVariable)}
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
root.render(
<App />
);
const myVariable = "hello world";

root.render does not call the component passed to it synchronously. Quick demo:
const App = () => {
console.log('App running');
};
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
console.log('Last line of the script');
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>
If root.render did call it synchronously, like you're doing with the first snippet, an error would indeed be thrown.
A more analogous version of the first snippet, given what React's doing, would be
function app() {
console.log(myVariable);
}
setTimeout(app);
const myVariable = "hello world";

Related

React returning Component displays [object Object]

I have a react component that has an event that gets called when a user clicks an item in a different component. The function that gets called is intended to display a Div on the web page. I can return an html string, to display the div, but I would rather but that HTML into its own component. My function gets called, but if I try to return a components (instead of raw html) it shows the div but not text. It just says [object Object]. The function that gets called looks like this:
function MyPage() {
const nodeHoverTooltip = (node) => {
return `<div>${node.name}</div>`;
//displays fine
};
const nodeClickDetails = (node) => {
if(node.nodeType === "somenode"){
return (<MyNodeDetails></MyNodeDetails>);
//this just displays [object, Object]
}else if (node.nodeType === "anotherNodeType"){
return `<div>Another Node Type Details</div>`;
//displays fine
}else{
return `<div>More Details</div>`;
//displays fine
}
};
return (
<div className="MyDiv">
<header className="header">Some Examples</header>
<section className="Main">
<ForceGraph
linksData={data.links}
nodesData={data.nodes}
nodeHoverTooltip={nodeHoverTooltip}
nodeClickDetails = {nodeClickDetails}
/>
</section>
</div>
);
}
export default MyPage;
MyNodeDetails.jsx Component:
function MyNodeDetails ({data}) {
return (
<div>
MyNodeDetails
</div>
)
}
export default MyNodeDetails
Is there a way to but the HTML into a component and not do raw HTML?
The function that's handling displaying the the HTML code is setting the innerHTML of an element to the html string being returned. When you convert a component, which is an object, to a string it gets converted to '[object Object]'. You need to use JSX instead of strings.
Here's an example to show the difference between both approaches. I doubt your rendering function is using dangerouslySetInnerHTML, but the idea of converting to string is what's important.
const { createRoot } = ReactDOM
const { useState } = React
const Test = () => <div>This is a test component</div>
const testHTML = '<div>This is a test html string</div>'
function App(){
const [component, setComponent] = useState()
const [html, setHTML] = useState();
function useJSX(){
setComponent(<Test />)
setHTML(testHTML)
}
function useHTML(){
setComponent(<div dangerouslySetInnerHTML={{__html: <Test/>}}></div>)
setHTML(<div dangerouslySetInnerHTML={{__html: testHTML}}></div>)
}
return (<div>
<button onClick={useJSX}>Use JSX</button>
<button onClick={useHTML}>Use HTML</button>
{component}
{html}
</div>)
}
const container = document.getElementById('root');
const root = createRoot(container);
root.render(<App tab="home" />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.production.min.js"></script>
<div id="root"></div>

React Context defaultValue returns undefined when no provider is wrapped around the component tree

I have been reading react docs to refresh my concepts. While reading, I came across this statement in context section:
The value argument passed to the function will be equal to the value
prop of the closest Provider for this context above in the tree. If
there is no Provider for this context above, the value argument will
be equal to the defaultValue that was passed to createContext().
So naturally, I created some snippets to actually test the scenario.
My entry point (App.js):
import "./styles.css";
import WearLacoste from "./wearLacoste";
import WearPrada from "./wearPrada";
import OutFitProvider from "./outfitContext";
export default function App() {
return (
<div className="App">
<h1>What to wear?</h1>
<OutFitProvider>
<WearPrada />
</OutFitProvider>
<WearLacoste />
</div>
);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Notice that I have two components. These components return a string rendered on the browser, telling which dress to wear. One of the component name <WearPrada /> is wrapped by the provider, the other (<WearLacoste/>) is not, to test the defaultValue scenario.
Following is my context file (outfitContext.js):
import React, { useContext, useState } from "react";
const MyOutfitContext = React.createContext("Lacoste");
//a custom hook to consume context
export const useOutfit = () => {
return useContext(MyOutfitContext);
};
const OutfitContextProvider = ({ children }) => {
const [outfit, setOutfit] = useState("Prada");
return (
<MyOutfitContext.Provider value={{ outfit }}>
{children}
</MyOutfitContext.Provider>
);
};
export default OutfitContextProvider;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
And finally both of my components, which do the exact same thing.
wearPrada.js :
import { useOutfit } from "./outfitContext";
const WearPrada = () => {
const { outfit } = useOutfit();
console.log("Outfit expects Prada", outfit);
return <h1>Wearing {outfit} RN </h1>;
};
export default WearPrada;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
wearLacoste.js :
import { useOutfit } from "./outfitContext";
const WearLacoste = () => {
const { outfit } = useOutfit();
console.log("Outfit expects Lacoste", outfit);
return <h1>Wearing {outfit} RN </h1>;
};
export default WearLacoste;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
My problem is:
The component wrapped in the provider is behaving as expected. It gets the state value initialised in context. But as claimed in the document, the component not wrapped in the provider still gets an undefined value instead of the defaultValue which is Lacoste (see outfitContext.js). Am I doing anything wrong here? Thanks in advance for the help.
It seems that you have mixed the object { outfit: string } with a simple string. The context value is expected to be an object everywhere but when setting the default value you use a string.
I believe what you want is const MyOutfitContext = React.createContext({ outfit: "Lacoste" });

What happens if pass function component as state variable?

What happens if I pass function component as state variable?
For example, would the below technically work? I tried it and it doesn't seem to work. I get the error props.children isn't a function. Any idea whether this would work?
const App = () => {
[functionComponent, setFunctionComponent] = useState((name) => <div>Hi, {name}</div>)
return <SomeContainerComponent>{functionComponent}</SomeContainerComponent>
}
const SomeContainerComponent = ({ children }) => {
return <div>{children({name: "John"})}</div>;
}
When you pass a function (whether a function that returns JSX or not) to useState, you're telling React to use lazy initialization - to call the function to compute the value for the initial state, rather than to set the state to be the function you passed in.
For what you want, you'd need to pass a function that returns a function, and also capitalize the state, since it's a component.
const App = () => {
const [FunctionComponent, setFunctionComponent] = React.useState(() => () => <div>Hello, World!</div>);
return <FunctionComponent />;
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>

Correct way of calling a component/function in React?

There are many ways to call a function in React. Can you explain when I should use <Function /> and when to use {Function()}? It seems like both do the same job.
Here is my sample code:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
{App()}
</React.StrictMode>,
rootElement
);
and inside the App.js I have:
import React from "react";
function App() {
return (
<div>
<h1>My favourite list</h1>
<ul>
<li>List 1</li>
<li>List 2</li>
<li>List 3</li>
</ul>
</div>
);
}
export default App;
Output of the following program:
TL;DR - Is it a component? Then use <App /> (or React.createElement).
The difference is subtle. Fundamentally, if the function is a component (like your App function), use it as a component: <App /> (or via React.createElement).
If the function is just a function that returns some elements, you can call it either way, but I would give it a name with a lower-case initial letter and call it directly. That said, it's very rare to want to write a function that returns some React elements rather than a pure functional component.
The difference relates to state and related concepts: A functional component can have state and such (via hooks), but a function you just call can't.
Here's an example trying to use hooks in the function, successfully via <Example />:
const {useState, useEffect} = React;
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
const handle = setInterval(() => {
setCount(c => {
if (c >= 10) {
clearInterval(handle);
} else {
++c;
}
return c;
});
}, 500);
return () => {
clearInterval(handle);
};
}, []);
return <div>{count}</div>;
}
ReactDOM.render(
<div><Example /></div>,
document.getElementById("root")
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.development.js"></script>
...and unsuccessfully via {Example()}:
const {useState, useEffect} = React;
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
const handle = setInterval(() => {
setCount(c => {
if (c >= 10) {
clearInterval(handle);
} else {
++c;
}
return c;
});
}, 500);
return () => {
clearInterval(handle);
};
}, []);
return <div>{count}</div>;
}
ReactDOM.render(
<div>{Example()}</div>,
document.getElementById("root")
);
Look in the real browser console for a detailed error message (this snippet uses the development libraries to get detailed messages).
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.0/umd/react-dom.development.js"></script>
Technically both are right, the only difference is, one is JavaScript based and another is JSX based. But the correct way is <Component /> - JSX way.
Another difference is the children. If you want to send props, you may as well go away with sending an object. What about complex children and nested children? Will you be creating them?
Technically, the JSX that you write gets transpiled into what you have showed - method 2.
In reality, if you have something like this:
class App extends React.Component {
render() {
return <p>Hello</p>;
}
}
The above gets transpiled into:
"use strict";
class App extends React.Component {
render() {
return /*#__PURE__*/React.createElement("p", null, "Hello");
}
}
In a nutshell: One is a JSX way and the other is JavaScript way. Most of the developers prefer JSX instead of JavaScript for readability and maintainability.

How to insert React element into string

I would like to add React component into the string as JSX like this
const name = World
const a = 'Hello, {name}'
return <div>{a}</div>
how can I get the following output
<div> Hello, World</div>
You don't need a string; instead, you also need to use JSX syntax (this won't work with a string):
const name = World;
const a = <>Hello, {name}</>;
return <div>{a}</div>;
you can just use another function component to get the other HTML part, and then add it to the main app return function inside {}, here is a working snippet:
function name() {
return ( World );
}
const App = () => {
const a = `Hello, `
return (
<div>{a} {name()}</div>
);
};
ReactDOM.render(
<App />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Categories