I'm having trouble updating the state of the child component. If i upload an image it doesn't appear in the ReactChromakeyedImage component. (which is a package i use)
import './App.css';
import ReactChromakeyedImage from 'react-chromakeyed-image';
import React, { useState, useEffect } from 'react';
function App() {
const [file, setFile] = useState();
function handleChange(e) {
console.log(e.target.files);
setFile(URL.createObjectURL(e.target.files[0]));
}
return (
<div className="App">
<h2>Add Image:</h2>
<input type="file" onChange={handleChange} />
<h3>Original</h3>
<img style={{width: "400px"}} src={file} />
<h3>Chromakeyed</h3>
<ReactChromakeyedImage src={file} style={{width: "400px"}} findColor="#3CC156" replaceColor="#FFFFFF" tolerance={70}/>
</div>
);
}
export default App;
CodePudding user response:
I figured it out. It's a bug in the lib. Internally, it checks the img
tag it uses inside to see if the complete
property has been set on the image HTML element. This goes to true
when the browser has painted the image. However, it only does this on every rerender, and it it also does not wait until it becomes true (its asynchronous). That's why its flaky.
Really the lib needs to be forked to fix, but there is a pretty horrible workaround you could do (this is really not nice at all, but it might be your only option unless you fork).
In this solution we attach a load listener using the DOM API (bad!) and then force a rerender, which triggers the library to check the completed property again.
Try this https://codesandbox.io/s/bold-voice-bhxw36?file=/src/App.js:0-1085.
import ReactChromakeyedImage from "react-chromakeyed-image";
import React, { useState, useRef, useLayoutEffect } from "react";
function App() {
const [file, setFile] = useState();
const containerRef = useRef();
const [, forceReload] = useState(0);
function handleChange(e) {
setFile(URL.createObjectURL(e.target.files[0]));
}
useLayoutEffect(() => {
if (!containerRef.current) return;
containerRef.current.querySelector("img").addEventListener("load", () => {
forceReload((x) => x 1);
});
}, [file]);
return (
<div className="App">
<h2>Add Image:</h2>
<input type="file" onChange={handleChange} />
<h3>Original</h3>
<img style={{width: "400px"}} src={file} />
<h3>Chromakeyed</h3>
{file && (
<div ref={containerRef}>
<ReactChromakeyedImage
src={file}
findColor="#3CC156"
replaceColor="#FFFFFF"
tolerance={70}
width="400"
height="400"
/>
</div>
)}
</div>
);
}
export default App;
CodePudding user response:
I do not know this library, and it could be a bug or simply that values are just stored and don't get updated if you change the value of the image. My advice would be just to make the component re-render to update it. The simplest way is to add a key
to it, whenever this key
changes the component will get re-rendered. I use file.name
in this example but any changing value would be fine.
<ReactChromakeyedImage key={file.name} src={file} style={{width: "400px"}} findColor="#3CC156" replaceColor="#FFFFFF" tolerance={70}/>