0

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

  • No one is going to read through all this code, I suggest you to provide a snack link or minimum reproducible example so people can help you better. – Vin Xi Nov 11 '22 at 06:25
  • https://snack.expo.dev/@jameheller98/carefree-cheese sorry but on expo everything alright but my project its not correct with useCallback :(((( – Tuan Nguyen Nov 11 '22 at 09:22

0 Answers0