I have a question about useCallBack in react?
- In useEffect I fetch data and set listDrawPath to context, first listDrawPath is array null [], after finish set listDrawPath second listDrawPath contains object as expected, but in function onDrawingStart the listDrawPath is still array null [], althought I have add depedency for useCallBack.
I don't know where I have mistaked, I used correct useCallBack as docs in react.
I use Macbook M1 to build app react native for IOS.
I happy if anyone help me about problem above. Code I show below.
// ~Libraries
import {Canvas, useTouchHandler} from '@shopify/react-native-skia';
import {Box, Factory, Row} from 'native-base';
import React, {useCallback, useEffect, useRef} from 'react';
import styles from './styles';
// ~Redux
import {useSelector} from 'react-redux';
import {selectDrawingSelected} from '~Drawing/Store/selectors';
// ~Utils
import {height} from '~Utils/dimensionUtils';
import {filterListByIds} from '~Utils/listUtils';
import {
findIndexContainPointInPaths,
mapperAttributesToPath,
Path,
} from '~Utils/skiaUtils';
// ~Components
import DrawingMasked from '~Drawing/Components/DrawingMasked';
import {useDrawingContext} from './DrawingContext';
import ToolBarColor from './ToolBarColor';
import ToolBarLine from './ToolBarLine';
// ~Constants
import {WIDTH_DRAWING} from '~Drawing/Configs/constants';
const CanvasFactory = Factory(Canvas);
const Drawing = () => {
const drawingSelected = useSelector(selectDrawingSelected);
const PointRef = useRef({x: 0, y: 0});
const lengthPathPrevious = useRef(0);
const drawIndexCurrent = useRef(-1);
const isDrawing = useRef(false);
const {
listCompletedPath,
listDrawPath,
strokeColor,
strokeType,
addCompletedPaths,
addAllDrawPath,
addAllCompletedPath,
updateDrawPath,
changeStrokeType,
changeStrokeColor,
} = useDrawingContext();
useEffect(() => {
if (drawingSelected) {
const newDrawPaths = drawingSelected.drawPaths.map(drawPath => ({
...drawPath,
path: mapperAttributesToPath(drawPath.typeShape, drawPath.attributes),
}));
const newCompletedPaths = drawingSelected.completedPaths.map(
completedPath => ({
...completedPath,
path: mapperAttributesToPath('path', {d: completedPath.path}),
}),
);
addAllDrawPath(newDrawPaths); // I update listDrawPath in here
addAllCompletedPath(newCompletedPaths);
}
}, [drawingSelected, addAllDrawPath, addAllCompletedPath]);
const onDrawingActive = useCallback(
({x, y}) => {
if (!isDrawing.current) return;
if (listCompletedPath.length === lengthPathPrevious.current) {
if (strokeType.strokeCap === 'butt') {
for (
let i = listCompletedPath.length - 1;
i > listCompletedPath.length - 3;
i--
) {
const path = listCompletedPath[i];
path.handleLineTo({
x: x - (listCompletedPath.length - 1 - i) * 10,
y: y - (listCompletedPath.length - 1 - i) * 10,
});
}
} else {
for (
let i = listCompletedPath.length - 1;
i > listCompletedPath.length - 2;
i--
) {
const path = listCompletedPath[i];
path.handleQuadTo(
{
x: PointRef.current.x - (listCompletedPath.length - 1 - i) * 5,
y: PointRef.current.y - (listCompletedPath.length - 1 - i) * 5,
},
{
x: x - (listCompletedPath.length - 1 - i) * 5,
y: y - (listCompletedPath.length - 1 - i) * 5,
},
);
}
}
}
PointRef.current = {x, y};
},
[listCompletedPath, strokeType],
);
console.log(listDrawPath, strokeType, strokeColor); // listDrawPath render 1st is [] and 2st is [object]
const onDrawingStart = useCallback(
({x, y}) => {
// But listDrawPath when use is still null array []
console.log(listDrawPath, strokeType, strokeColor);
drawIndexCurrent.current = findIndexContainPointInPaths(
listDrawPath,
{
x,
y,
},
);
if (drawIndexCurrent.current >= 0) {
const {SvgComponent, svgProperties, ...restStrokeType} =
strokeType;
if (restStrokeType.strokeCap === 'butt') {
const completedPath = new Path(
{x, y},
restStrokeType,
strokeColor,
);
const completedPath2 = new Path(
{x: x - 10, y: y - 10},
restStrokeType,
strokeColor,
);
lengthPathPrevious.current = listCompletedPath.length + 2;
isDrawing.current = true;
addCompletedPaths([completedPath, completedPath2]);
updateDrawPath(drawIndexCurrent.current, [
completedPath.getId(),
completedPath2.getId(),
]);
} else {
const completedPath = new Path(
{x, y},
restStrokeType,
strokeColor,
);
lengthPathPrevious.current = listCompletedPath.length + 1;
isDrawing.current = true;
addCompletedPaths([completedPath]);
updateDrawPath(drawIndexCurrent.current, [completedPath.getId()]);
}
}
PointRef.current = {x, y};
},
[
listDrawPath, // listDrawPath is dependency of useCallback
strokeColor,
strokeType,
listCompletedPath.length,
addCompletedPaths,
updateDrawPath],
);
const onDrawingEnd = useCallback(() => {
isDrawing.current = false;
PointRef.current = {x: 0, y: 0};
}, []);
const touchHandler = useTouchHandler(
{
onActive: onDrawingActive,
onStart: onDrawingStart,
onEnd: onDrawingEnd,
},
[onDrawingActive, onDrawingStart, onDrawingEnd],
);
return (
<Row>
<Box w={`${height}px`} mx={`${(WIDTH_DRAWING - height) / 2}px`}>
<CanvasFactory flex={1} onTouch={touchHandler} style={styles.canvas}>
{listDrawPath.map(drawPath => (
<DrawingMasked
key={drawPath.id}
drawPath={drawPath}
listCompletedPath={filterListByIds(
listCompletedPath,
drawPath.completedPathIds,
)}
/>
))}
</CanvasFactory>
</Box>
<ToolBarColor changeStrokeColor={changeStrokeColor} />
<ToolBarLine
strokeColor={strokeColor}
strokeType={strokeType}
changeStrokeType={changeStrokeType}
/>
</Row>
);
};
export default Drawing;
I want to know where I mistake and why listDrawPath not update in function onDrawingStart