Reputation: 2970
Hi I have a class component as shown below:
class SomeComponent extends Component {
componentDidMount = () => {
const divElement = document.getElementbyId('id'); // this element could take a few seconds to load
if (props.something1 && props.something2) {
..do something with divElement's width
}
}
render() {
return ....
}
}
I want to wait until divElement is loaded, or trigger an event when divElement is loaded so I can do my calculation later, tried adding setTimeout which did not work
Upvotes: 8
Views: 22766
Reputation: 801
Here's my hook version to @hao-wu's answer
const useGetElementAsync = (query) => {
const [element, setElement] = useState(null);
useEffect(() => {
(async () => {
let element = await new Promise((resolve) => {
function getElement() {
const element = document.querySelector(query);
if (element) {
resolve(element);
} else {
console.count();
// Set timeout isn't a must but it
// decreases number of recursions
setTimeout(() => {
requestAnimationFrame(getElement);
}, 100);
}
};
getElement();
});
setElement(element);
})();
}, [query]);
return element;
};
To use it:
export default function App() {
const [isHidden, setIsHidden] = useState(true);
const element = useGetElementAsync(".myElement");
// This is to simulate element loading at a later time
useEffect(() => {
setTimeout(() => {
setIsHidden(false);
}, 1000);
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
{!isHidden && (
<h2 className="myElement">My tag name is {element?.tagName}</h2>
)}
</div>
);
}
Here's the codesandbox example
Upvotes: 2
Reputation: 556
The answer of @hao-wu is great. Just if anyone wonders how to use it with hooks here is my snippet.
const Editor = () => {
const [editor, setEditor] = useState<SimpleMDE | null>(null);
useEffect(() => {
(async () => {
const initialOptions = {
element: await getElementByIdAsync(id),
initialValue: currentValueRef.current
};
setEditor(
new SimpleMDE({
element: await getElementByIdAsync(id),
initialValue: currentValueRef.current
})
);
})();
}, [id]);
// Other effects that are looking for `editor` instance
return <textarea id={id} />;
};
Otherwise, the constructor of SimpleMDE cannot find an element and everything is broken :) I guess you can adjust it to your use-case quite easily.
Most of the time useRef
just works, but not in this scenario.
Upvotes: 0
Reputation: 85573
You need to use the componentDidUpdate
hook instead of componentDidMount
hook. And better to use ref rather than getting div element by it's id:
componentDidUpdate() {
if (props.something1 && props.something2) {
// use divElementRef to interact with
}
}
Upvotes: 0
Reputation: 20867
This is a dumb solution but it gets its jobs done:
const getElementByIdAsync = id => new Promise(resolve => {
const getElement = () => {
const element = document.getElementById(id);
if(element) {
resolve(element);
} else {
requestAnimationFrame(getElement);
}
};
getElement();
});
To use it:
componentDidMount = async () => {
const divElement = await getElementByIdAsync('id');
if (props.something1 && props.something2) {
// ..do something with divElement's width
}
}
Upvotes: 11
Reputation: 1075199
Two answers for you:
If the element is rendered by your component, use a ref.
MutationObserver
(if the element is outside React)If the element is completely outside the React part of your page, I'd look for it with getElementById
as you are, and if you don't find it, use a MutationObserver
to wait for it to be added. Don't forget to remove the mutation observer in componentWillUnmount
.
That would look something like this:
componentDidMount = () => {
const divElement = document.getElementbyId('id');
if (divElement) {
this.doStuffWith(divElement);
} else {
this.observer = new MutationObserver(() => {
const divElement = document.getElementbyId('id');
if (divElement) {
this.removeObserver();
this.doStuffWith(divElement);
}
});
this.observer.observe(document, {subtree: true, childList: true});
}
}
componentWillUnmount = () => {
this.removeObserver();
}
removeObserver = () => {
if (this.observer) {
this.observer.disconnect();
this.observer = null;
}
}
(You may have to tweak that, it's off-the-cuff; see the MutationObserver
documentation for details.)
Upvotes: 18
Reputation: 13966
You can do something like;
componentDidMount() {
// Triggering load of some element
document.querySelector("#id").onload = function() {
// Write your code logic here
// code here ..
}
}
Reference https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onload
Upvotes: -1