Calling function when useState s called? - javascript

Parent Component
const TopPage = (props) =>{
const [fileUrl, setFileUrl ] = useState("");
const onSetFileUrl = (data) =>{
setFileUrl(data);
}
return (
<div>
<ImagePreview fileUrl={fileUrl}></ImagePreview>
</div>
);
}
Child Component
import React from 'react'
const ImagePreview = (props) =>{
const layer = null;
const onImageChange = () =>{
var layer = document.getElementById('layer');
var ctx = layer.getContext('2d');
console.log("image is changed");
const chara = new Image();
chara.src = props.fileUrl;
chara.onload = () => {
ctx.drawImage(chara, 0, 0);
};
}
return (<div>Image Preview:{props.fileUrl}
<canvas id="layer" style={{width: 100, height: 100,backgroundColor:"yellow"}} />
</div>);
};
export default ImagePreview;
Parent Component gives the fileUrl to Child Component.
When data changed, fileUrl is given to Child Component via props.
However, at the same time I want to draw this in canvas.
So I need to call onImageChange() of Child Component when fileUrl is changed.
How can I do this ?

Related

react, To call a function from a child component to a parent component

I want to run a function on the parent component in the child component.
Eventually, I want to have the function run when the scroll goes to that position.
UseImperativeHandle was used, but props did not apply. Is there a way to apply props in the useImperativeHandle?
Also, is it correct to use IntersectionObserver this way?
child Components
function Percent(props, ref) {
useImperativeHandle(ref,() => ({
percentst: () => {
var cnt = document.querySelectorAll(".count")[props.num];
var water = document.querySelectorAll(".water")[props.num];
var percent = cnt.innerText;
var interval;
interval = setInterval(function () {
percent++;
cnt.innerHTML = percent;
water.style.transform = 'translate(0' + ',' + (100 - percent) + '%)';
if (percent == props.percent) {
clearInterval(interval);
}
}, 80);
}
}));
}
export default forwardRef(Percent);
parent component
function About(props) {
const containerRef = useRef();
const myRef = useRef();
const [isVisible, setIsVisible] = useState(false);
const callbackFunction = (entries) => {
const [entry] = entries;
setIsVisible(entry.isIntersecting);
};
const options = {
root: document.getElementById('skills'),
rootMargin: '0px',
threshold: 1
};
useEffect(() => {
const observer = new IntersectionObserver(callbackFunction, options);
console.log(containerRef.current)
if (containerRef.current) observer.observe(containerRef.current);
return () => {
myRef.current.percentst()
if (containerRef.current) observer.unobserve(containerRef.current);
};
}, [containerRef, options]);
return(
<div ref={containerRef}></div>
<Percent ref={myRef} />
)
}
export default About;
Two ways of doing this
Pass your method as a prop to the child (this may not be desirable)
Use custom event listeners/triggers
There are some packages out there that will help with events such as https://www.npmjs.com/package/react-custom-events

Import Variable from React Component to Javascript File

I have a react component Button in which I have two states name and players. I want to import these states into another JS file. This file is a vanilla javascript file and not a component.
Here are the codes:
Button.js
import {useState} from "react"
import {buttonContent} from '../buttonContent'
import {correctAnswer} from "../pageTraversal"
import {verifyResults} from "../verifyResults"
const Button = ({textInput, updateApp, updatePage, updateError}) => {
const [pageTitle, updateButton] = useState("home-page")
const [textVisible, textVisibility] = useState("")
const [disabled, changeClick] = useState(false)
const [cursor, changeCursor] = useState("pointer")
const [name, updateName] = useState('')
const [players, updateFriends] = useState('')
const startPages = ["home-page", "welcome", "scenario", "the-adventure-begins"]
const navigatePage = () => {
if (startPages.includes(pageTitle)){
changeCorrectPage()
return
}
if(textInput===""){
updateError("")
return
}
else{
updateError("remove")
}
const response = verifyResults(pageTitle, textInput)
if (pageTitle === "instructions"){
updateName(response["player"])
updateFriends(response["friends"])
changeCorrectPage()
}
if(response === "correct"){
changeCorrectPage()
}
}
const changeCorrectPage = () => {
const page = correctAnswer[pageTitle]
updateApp(page)
updatePage(page)
changeButton(page)
}
const changeButton = (page) => {
textVisibility("hide")
changeClick(true)
changeCursor("auto")
setTimeout(() => {
updateButton(page)
}, 2500)
setTimeout(() => {
textVisibility("show")
}, 4500)
setTimeout(() => {
changeClick(false)
changeCursor("pointer")
}, 6500)
}
return (
<div>
<button className={`button mx-auto`}
id={(pageTitle==="home-page" ? "home-button" : "")}
style={{
"cursor": {cursor},
}}
disabled={disabled}
onClick={navigatePage}
>
<h1 className={`button-text ${textVisible}`}>
{buttonContent[pageTitle]}
</h1>
</button>
<Players name={name} friends={friends} />
</div>
)
}
export default Button
I want to import the two states name and players into another javascript file below where I want to use them as normal variables.
const width = window.innerWidth;
const height = window.innerHeight;
let textSize = ''
if(height > width){
textSize = "small"
}
console.log(name)
console.log(players)
I have tried importing the variables as normal variables, but that doesn't work here. Please suggest a suitable way of doing this.
There is no official approach for this link of problem. But I suggest to use browser storages like localStorage to handle this.
So:
1) Save the states on localStorage based on their changes with useEffect()
useEffect(()=>{
localStorage.setItem("name", name);
localStorage.setItem("players", players);
},[name,players])
2) Get the states data with localStorage everywhere that you want
const width = window.innerWidth;
const height = window.innerHeight;
let textSize = ''
if(height > width){
textSize = "small"
}
console.log(localStorage.getItem("name"))
console.log(localStorage.getItem("players"))

React Child Component does not update properties passed from Parent Component

I have a parent component and a child component as function components in react.
Parent Component
const Parent = () => {
const selectedProduct = 0;
//const [servicesList, setServicesList] = useState<IService[]>([]);
var servicesList = [] as IService[];
var operationalProcessSelector = [] as IOperationalProcessSelector[];
useEffect(()=>{
fetchServicesList(840);
}, []);
const fetchServicesList = async (quotationId: number) => {
const SERVICE_LIST_API_URL = `API URL...`;
await axios.get(SERVICE_LIST_API_URL, {headers:API_REQ_HEADERS})
.then((response:any)=>{
if(response && response.data && response.data.length > 0 && response.data[0]){
let product = response.data[selectedProduct];
let operationalProcesses = product.OperationalProcess;
updateServiceList(operationalProcesses);
}
});
};
const updateServiceList = (operationalProcesses: any) => {
operationalProcesses.forEach((operationalProcessItem: any)=>{
operationalProcessItem.Services.forEach((serviceItem: any) => {
serviceItem.LineEditState = 'edit';
serviceItem.ShowContent = true;
});
});
const flattenedArray: IService[] = operationalProcesses.reduce(
(accumulator: IService[], value: any) => accumulator.concat(value.Services),
[] as IService[]);
//servicesList(flattenedArray);
servicesList = flattenedArray;
console.log('Update Services', servicesList); // It retrieves and outputs data here
};
return <ServiceGrid OperationalProcessSelector={operationalProcessSelector} />
}
export default Parent
Child Component
const ServiceGrid = (props: Prop) => {
console.log("Component ServiceGrid generated");
const [tableData, setTableData] = useState<IService[]>(props.ServicesList);
useEffect(()=>{
console.log('component', tableData); // It does not return the value
});
return (
<div>{tableData.map((e: any)=>e.childData)}</div> // It does not render the value here
);
};
export default ServiceGrid;
interface Prop {
OperationalProcessSelector: IOperationalProcessSelector[],
ServicesList: IService[]
}
Question: why tableData in child component does not load?
I am not sure whether I should use State or just a plain variable here. But once I receive the value to the child, I might update the child variable and it should automatically reflect in the child component.
PS:
I have tried useState() in both parent and child components as well.
Parent Component variables
const [servicesList, setServicesList] = useState<IService[]>([]);
const [operationalProcessSelector, setOperationalProcessSelector] = useState<IOperationalProcessSelector[]>([]);
even after using setTableData function it does not reflect.
Child Component variables
useEffect(()=>{
setTableData(props.ServicesList);
// setOperationalProcessSelector(props.OperationalProcessSelector);
console.log('component', props.ServicesList);
}, []);
you should pass props.ServicesList to useEffect dependency array.
useEffect(()=>{
setTableData(props.ServicesList);
// setOperationalProcessSelector(props.OperationalProcessSelector);
console.log('component', props.ServicesList);
}, [props.ServicesList]);

React ref is not set correctly inside useEffect

I have a parent component that passes its size as a prop and renders a child component containing a canvas. It looks like this :
const CanvasContainer = () => {
const containerRef = useRef(null);
const [canvasWidth, setCanvasWidth] = useState(0);
const [canvasHeight, setCanvasHeight] = useState(0);
useLayoutEffect(() => {
const container = containerRef.current;
if (!container) return;
setCanvasWidth(container.offsetWidth);
setCanvasHeight(container.offsetHeight);
}, []);
return (
<div ref={containerRef}>
<Canvas canvasWidth={canvasWidth} canvasHeight={canvasHeight} />
</div>
);
};
The child component saves a ref to the canvas, creates the canvas context, modifies some properties of the canvas context, and saves this context to another ref.
However, when the context gets saved to the ref, it is saved with its default properties instead of the properties that I had modified.
const Canvas = (props) => {
const canvasRef = useRef(null);
const contextRef = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const context = canvas.getContext('2d');
if (!context) return;
context.lineCap = 'round';
console.log(contextRef.current); // Logs null
contextRef.current = context;
console.log(contextRef.current); // Logs CanvasRenderingContext2D but lineCap is still set to its default value ('butt' instead of 'round')
}, []);
const { canvasWidth, canvasHeight } = props;
return (
<canvas
ref={canvasRef}
width={canvasWidth}
height={canvasHeight}
/>
);
};
I tried to replace useEffect with useLayoutEffect but it doesn't change anything.
I noticed that this bug is not occurring when I add canvasWidth and canvasHeight to the useEffect dependency array.
Is this behavior expected or is this some kind of bug ?
Live Example:
const {useRef, useState, useEffect, useLayoutEffect} = React;
const CanvasContainer = () => {
const containerRef = useRef(null);
const [canvasWidth, setCanvasWidth] = useState(0);
const [canvasHeight, setCanvasHeight] = useState(0);
useLayoutEffect(() => {
const container = containerRef.current;
if (!container) return;
setCanvasWidth(container.offsetWidth);
setCanvasHeight(container.offsetHeight);
}, []);
return (
<div ref={containerRef}>
<Canvas canvasWidth={canvasWidth} canvasHeight={canvasHeight} />
</div>
);
};
const Canvas = (props) => {
const canvasRef = useRef(null);
const contextRef = useRef(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const context = canvas.getContext('2d');
if (!context) return;
context.lineCap = 'round';
console.log(context.lineCap);
console.log(contextRef.current); // Logs null
contextRef.current = context;
console.log(contextRef.current); // Logs CanvasRenderingContext2D but lineCap is still set to its default value ('butt' instead of 'round')
console.log(contextRef.current.lineCap);
}, []);
const [isDrawing, setIsDrawing] = useState(false);
const startDrawing = (event) => {
const { offsetX, offsetY } = event.nativeEvent;
if (!contextRef.current) return
console.log(contextRef.current.lineCap); // Logs 'butt' instead of 'round'
contextRef.current.beginPath();
contextRef.current.moveTo(offsetX, offsetY);
setIsDrawing(true);
};
const draw = (event) => {
if (!isDrawing) return;
const { offsetX, offsetY } = event.nativeEvent;
if (!contextRef.current) return
contextRef.current.lineTo(offsetX, offsetY);
contextRef.current.stroke();
};
const stopDrawing = () => {
if (!contextRef.current) return
contextRef.current.closePath();
setIsDrawing(false);
};
const { canvasWidth, canvasHeight } = props;
return (
<canvas
ref={canvasRef}
width={canvasWidth}
height={canvasHeight}
// Event Handlers
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
/>
);
};
ReactDOM.render(<CanvasContainer />, document.getElementById("root"));
#root > div {
height: 200px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

React Native HOC component

I am trying to do a withCache HoC component but having some problems...
Thats the HoC:
// HOC for cached images
const withCache = (Component) => {
const Wrapped = (props) => {
console.log(props);
const [uri, setUri] = useState(null);
useEffect(() => {
(async () => {
const { uri } = props;
const name = shorthash.unique(uri);
const path = `${FileSystem.cacheDirectory}${name}`;
const image = await FileSystem.getInfoAsync(path);
if (image.exists) {
console.log("Read image from cache");
setUri(image.uri);
return;
} else {
console.log("Downloading image to cache");
const newImage = await FileSystem.downloadAsync(uri, path);
setUri(newImage.uri);
}
})();
}, []);
return <Component {...props} uri={uri} />;
};
Wrapped.propTypes = Component.propTypes;
return Wrapped;
};
export default withCache;
The thing is that "Component" is a custom Image component with specific propTypes and defaultProps.
How do I use this component? I have tried:
const CachedImage = withCache(<MyCustomImage uri={"https://..."} height={100} ripple />)
...
return (<CachedImage />)
but not working :( What I want is to pass a boolean prop to my custom image component named "cached", and if true return the custom image component wrapped in the HOC
In order to use the HOC, you would create the instance outside of the functional component like
const CachedImage = withCache(MyCustomImage)
and use it like
const MyComp = () => {
...
return (<CachedImage uri={"https://..."} height={100} ripple />)
}
Final implementation of the HoC, in case someone finds it useful in the future.
import React, { useState, useEffect } from "react";
import shorthash from "shorthash";
import * as FileSystem from "expo-file-system";
// HOC for cached images
const withCache = (Component) => {
const Wrapped = (props) => {
console.log(props);
const [uri, setUri] = useState(null);
useEffect(() => {
(async () => {
const { uri } = props;
const name = shorthash.unique(uri);
const path = `${FileSystem.cacheDirectory}${name}`;
const image = await FileSystem.getInfoAsync(path);
if (image.exists) {
console.log("Read image from cache");
setUri(image.uri);
return;
} else {
console.log("Downloading image to cache");
const newImage = await FileSystem.downloadAsync(uri, path);
setUri(newImage.uri);
}
})();
}, []);
// Needs to have the final uri before render the image
return uri && <Component {...props} uri={uri} />;
};
Wrapped.propTypes = Component.propTypes;
return Wrapped;
};
export default withCache;

Categories

Resources