Event Handling in React js - reactjs

const [show, setShow] = useState(true)
const showDiv = () => {
if (show) {
document.getElementsByClassName("skills-hero")[0].style.display = "flex"
}
else if (!show) {
document.getElementsByClassName("skills-hero")[0].style.display = "none"
}
}
This is simple logic but it is not working and not giving any error too.
I just don't under stand why it doesn't working

There are two possibilities why your code is not working:
The skill-hero element is defined inside the 'root' element (where you are attaching your Reactjs app. assuming you are attaching your react app to the real DOM. Hence the element is getting overridden by the react app.
function showDiv is not returning anything, I presume react will complain if you call the function inside the return(jsx) function.
To make your example work:
Inside Index.html
<div class="skills-hero">
<div class="p">1</div>
<div class="p">2</div>
</div>
<div id="root"></div>
Inside Javascript
const MyFunc = () => {
const [show, setShow] = React.useState(true);
const showDiv = () => {
if (show) {
document.getElementsByClassName("skills-hero")[0].style.display = "flex";
} else if (!show) {
document.getElementsByClassName("skills-hero")[0].style.display = "none";
}
return "some dummy text";
};
return showDiv();
};
ReactDOM.render(<MyFunc />, document.getElementById("root"));
Note: This method frowned upon. ^^^
You should avoid direct DOM manipulation in ReactJs mainly because:
performance
security (XSS)
and ReactDOM does the dom-manipulation for you efficiently.
it makes out lives way easier 😼
etc...
const [show, setShow] = useState(true);
const showDiv = () => show ? <div className="skills-hero"> </div> : null

You can't select jsx elements with getElementsByClassName because they are not in the real dom. Your real DOM is empty in React.
You might want to do conditional rendering instead;
show && <div className="skills-hero"></div>
something like this.

Related

Passed function as prop and is undefined

I'm trying to passed function toggle and is undefined. How to fix that ?
function page() {
const [open, setOpen] = React.useState(false);
const handleToggle = () => {
setOpen(!open);
};
return (
<div>
<Sidebar state={open} callback={handleToggle} />
</div>
);
}
export default page;
//Passing to Sidebar component //
const Sidebar = ({callback,state}:any) => {
}
I would consider just passing props on the header of Sidebar component (something like):
const Sidebar = (props) => {...}
Then you can access props.callback and props.state inside your code.
Another piece of advice I could give you is to not use the state itself to change on the handler, but use the previous snapshot, for example:
const handleToggle = () => {
setOpen(prevIsOpen => !prevIsOpen);
};
This way you could avoid some random bugs. (See https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous)
Don't know if this was of any help but hope it was (not a lot of experience in React).

React response get data

I wanted to add the data of a Axios response into my useState Array. The problem is that I don't know how to get the data out of the then() function. Basically what I want to do is save the Axios response so that I can use it in my Array. (I'm trying React for the first time)
for (var i = 1; i <= props.max; i++) {
const response = Axios.get("http://localhost:3001/content", {id: 1});
response.then((res) => {
console.log(res.data[0].title)
})
blogs.push({title: "TITLE HERE", text: "text", author: "author", date: "date"}); //I want to insert the title here
}
I already tried:
const [title, setTitle] = useState();
for (var i = 1; i <= props.max; i++) {
const response = Axios.get("http://localhost:3001/content", {id: 1});
response.then((res) => {
setTitle(res.data[0].title)
})
}
Heres the complete function:
import React, { useEffect, useState, express, memo } from "react";
import './network.css';
import Axios from 'axios';
function Content(props) {
const [blogs, setBlogs] = useState([]);
const [title, setTitle] = useState();
/**const [text, setText] = useState();
const [author, setAuthor] = useState();
const [date, setDate] = useState();*/
for (var i = 1; i <= props.max; i++) {
const response = Axios.get("http://localhost:3001/content", {id: 1});
response.then((res) => {
console.log(res.data[0].title)
})
blogs.push({title: "TITLE", text: "text", author: "author", date: "date"}); //I want to insert the title here
}
return (
<div>
{blogs.map((blog) => (
<div class="card">
<div class="card-body">
<h4>{blog.title}</h4>
<p>{blog.text}</p>
<div class="user">
<img alt="user" id="image"/>
<div class="user-info">
<h5>{blog.author}</h5>
<small>{blog.date}</small>
</div>
</div>
</div>
</div>
))}
</div>
);
}
export default Content;
Please add your fetch logic on useEffect hook. Otherwise, fetch logic will be executed in every re-render.
Your app may get frozen.
And you should not change state variable blogs by blogs.push....
use setBlogs function.
And please use className instead of class in DOM.
I see many problems in the code and strongly to read react help before writing any code.
Anyway updated code is here.
function Content(props) {
const [blogs, setBlogs] = useState([]);
const [title, setTitle] = useState();
/**const [text, setText] = useState();
const [author, setAuthor] = useState();
const [date, setDate] = useState();*/
useEffect(() => {
for (var i = 1; i <= props.max; i++) {
const response = Axios.get("http://localhost:3001/content", {id: 1});
response.then((res) => {
setBlogs(res.data);
})
}
}, []);
return (
<div>
{blogs.map((blog, key) => (
<div className="card" index={key}>
<div className="card-body">
<h4>{blog.title}</h4>
<p>{blog.text}</p>
<div className="user">
<img alt="user" id="image"/>
<div className="user-info">
<h5>{blog.author}</h5>
<small>{blog.date}</small>
</div>
</div>
</div>
</div>
))}
</div>
);
}
export default Content;
You should useEffect hook for fetching data and .then should set it to the state. UseEffect will fetch data when the component is rendered, and store it in the state. Then you can display it in jsx. I recommend reading this article (which shows how to do it with Axios).
https://www.freecodecamp.org/news/how-to-use-axios-with-react/
Since Axios is a promise-based HTTP client library you will have to deal with the data only after the response is resolved. This means you have two options . 1) use setBlog inside then function completely (which is mentioned by other answers already)
2) Use async-await syntax(which is just syntactic sugar to the Promise.then() and Promise.catch())
Another thing you need to keep in mind is to try and treat your react state arrays as immutable. So you need to use the spread operator instead of the traditional push method because the push method directly mutates your state array and can lead to some dangerous and error-prone code.
It is recommended that you make you react 'state' changes in the useEffect hook or inside a function in the functional component. triggering state changes in the manner you have done can lead to infinite rendering and cause a Runtime Error.
When creating a list in the UI from an array with JSX, you should add a key prop to each child and to any of its’ children. (the key is recommended 'NOT 'to be the index of the array) Below is the code sample to set your array into the state
useEffect(()=>{
(async()=>{
for(let i=0;i<=props.max;i++){
let response = await Axios.get("http://localhost:3001/content",{id:1});
setBlogs((blog)=>[...blog,response.data]);
}
})();
});

Pass a reference to a DOM element into React

I have a reference to a DOM element (from a 3rd-party library) and need to render it it React. I can think of a couple ways do this, but I'm hoping to find a solution that's more straightforward and doesn't require converting the element to a string first. For example:
const el = document.createElement('div');
el.textContent = 'test';
// This works but requires converting the element to a string first.
const reactEl = (
<div dangerouslySetInnerHTML={{__html: el.innerHTML}} />
);
// Something like this should also work, but it seems overly complicated.
function MyComponent() {
const myRef = useRef();
useEffect(() => {
myRef.current.appendChild(el);
}, []);
return (
<div ref={myRef} />
);
}
Is there another, more straightforward way to insert a DOM element into jsx?
Your second method is the correct way to do it in React. It may seem complicated, but it's not.
function MyComponent() {
const myRef = useRef();
useEffect(() => {
myRef.current.appendChild(el);
}, []);
return (
<div ref={myRef} />
);
}

get iframe elements in react using ref

I have a component contains iframe and I want to access its content in react, I used ref to handle the iframe, how can I get all anchors tags from the iframe
here is my code :
const GridGenerator = () => {
const [loading, setLoading] = useState(true);
const gridIframe = useRef(null);
function handleIframe() {
setLoading(false);
const iframeItem = gridIframe.current;
const anchors = iframeItem.contentWindow.getElementsByTagName("a");
}
return (
<div>
{loading ? <div className={styles.loader} /> : null}
<iframe
title="Grid Generator"
ref={gridIframe}
src="https://collectearth.users.earthengine.app/view/collect-earth-grid-generator"
width="100%"
height="1000px"
frameBorder={0}
onLoad={handleIframe}
/>
<Link to={routes.HOME}>Go Back</Link>
</div>
);
};
so, it works well until :
const iframeItem = gridIframe.current;
and it returns iframe tag, but this line does not work well
const anchors = iframeItem.contentWindow.getElementsByTagName("a");
any solution ? Thank you
You need to access the document of contentWindow in order to get the elements, Window interface does not have any method called getElementsByTagName.
So, instead of
const anchors = iframeItem.contentWindow.getElementsByTagName("a");
you should do
const anchors = iframeItem.contentWindow.document.getElementsByTagName("a");
For me adding to Window worked, instead of Document:
Without bind():
function handleSubmit(e) {
alert('yay 1!', e);
}
iframeRef.current?.contentWindow?.addEventListener('click', handleSubmit, false);
With bind():
function handleSubmit2() {
alert('yay 2!', this.ele);
}
iframeRef.current?.contentWindow?.addEventListener('click', handleSubmit2.bind({ ele: iframeRef }), false);

React hooks. Update component passed as param via onClick after one of it's prop was changed

Hi guys) I have a strange question may be, but I'm at a dead end.
I have my own custom hook.
const useModal = (Content?: ReactNode, options?: ModalOptions) => {
const { isOpen, close: contextClose, open: contextOpen, setContent } = useContext(
ModalContext,
)
const [customOpenContent, setCustomOpenContent] = useState<ReactNode>()
const showModal = useCallback(
(customContent?: ReactNode) => {
if (!isNil(customContent)) {
setCustomOpenContent(customContent)
contextOpen(customContent, options)
} else contextOpen(Content, options)
},
[contextOpen, Content, options],
)
const hideModal = useCallback(() => {
contextClose()
}, [contextClose])
return { isOpen, close: hideModal, open: showModal, setContent }
}
It is quite simple.
Also i have component which uses this hook
const App: React.FC = () => {
const [loading, setLoading] = useState(false)
const { open } = useModal(null, { deps: [loading] })
useEffect(() => {
setTimeout(() => {
setLoading(true)
}, 10000)
})
const buttonCallback = useCallback(() => {
open(<Button disabled={!loading}>Loading: {loading.toString()}</Button>)
}, [loading, open])
return (
<Page title="App">
<Button onClick={buttonCallback}>Open Modal</Button>
</Page>
)
}
Main problem is - Button didn't became enabled because useModal hook doesn't know anything about changes.
May be you have an idea how to update this component while it's props are updated? And how to do it handsomely ))
Context isn't the best solution to this problem. What you want is a Portal instead. Portals are React's solution to rendering outside of the current React component hierarchy. How to use React Portal? is a basic example, but as you can see, just going with the base React.Portal just gives you the location to render.
Here's a library that does a lot of the heavy lifting for you: https://github.com/wellyshen/react-cool-portal. It has typescript definitions and provides an easy API to work with.
Here's your example using react-cool-portal.
import usePortal from "react-cool-portal";
const App = () => {
const [loading, setLoading] = useState(false);
const { Portal, isShow, toggle } = usePortal({ defaultShow: false });
useEffect(() => {
setTimeout(() => {
setLoading(true);
}, 10000);
});
const buttonCallback = useCallback(() => {
toggle();
}, [toggle]);
return (
<div title="App" style={{ backgroundColor: "hotpink" }}>
<button onClick={buttonCallback}>
{isShow ? "Close" : "Open"} Modal
</button>
<Portal>
<button disabled={!loading}>Loading: {loading.toString()}</button>
</Portal>
<div>{loading.toString()}</div>
</div>
);
};
Basic CodeSandbox Example
There are more detailed ones within the react-cool-portal documentation.
For more detail of the issues with the Context solution you were trying, is that React Elements are just a javascript object. React then uses the object, it's location in the tree, and it's key to determine if they are the same element. React doesn't actually care or notice where you create the object, only it's location in the tree when it is rendered.
The disconnect in your solution is that when you pass the element to the open function in buttonCallback, the element is created at that point. It's a javascript object that then is set as the content in your context. At that point, the object is set and won't change until you called open again. If you set up your component to call open every time the relevant state changes, you could get it working that way. But as I mentioned earlier, context wasn't built for rendering components outside of the current component; hence why some really weird workarounds would be required to get it working.

Resources