React How creating a hook with a function with a parameter passed as a prop that dose not reference the parameter at hooks creation

Viewed 22

I have a hook called DragAndSize and I have my app.

the the app looks like

function App(){

    const [toolTips, set_toolTips] = useState([]);

    console.log(`------------------------- reload -------------------------`);
    return(
        <div>
            <button
                onClick={()=>{
                    let temp_toolTips = [... toolTips];
                    temp_toolTips.push(
                        <DragAndSize
                            dragAndSize={{
                                left: 20,
                                top: 20,
                                width : 400,
                                height : 400,
                                hidden : false,
                                zIndex : 1000
                            }}
                            styleParent={{
                                backgroundColor: "blue",
                                borderRadius: "10px",
                                borderColor : "balck",
                                borderWidth : "thick",
                            }}
                            closeFunction={()=>{
                                console.log(toolTips)
                            }}
                        >
                        </DragAndSize>
                    )
                    set_toolTips(temp_toolTips);
                }}
            >
                add
            </button>

            {toolTips.map((i, index)=>{return(
                <div key={index}>
                    {i}
                </div>
            )})}
        </div>
    )
}

the DragAndSize

function DragAndSize(props){

    /*------------------------- util -------------------------*/
    let get_highest_index = ()=>{
        let elements = document.getElementsByTagName("*");
        let highest_index = 0;

        for(let i = 0; i < elements.length -1; i++){
            if(Boolean(parseInt(elements[i].style.zIndex))){
                if(parseInt(elements[i].style.zIndex) > highest_index){
                    highest_index = parseInt(elements[i].style.zIndex);
                }
            }
        }
        return(highest_index + 1)
    }

    /*------------------------- mouse -------------------------*/
    const [trackMouse, set_trackMouse] = useState(false);
    const [mouseDown, set_mouseDown] = useState({x: 0, y: 0});
    const [mouseUp, set_mouseUp] = useState({x: 0, y: 0});

    /*-------------------- ToolTip --------------------*/
    let dragAndSize = props.dragAndSize ?? {
        left: 100,
        top: 100,
        width : 200,
        height : 200,
        hidden : false,
        zIndex : 100
    }
    const [trackToolTipMove, set_trackToolTipMove] = useState(false);
    const [trackToolTipSize, set_trackToolTipSize] = useState(false);
    const [dragAndSizeStart, set_dragAndSizeStart] = useState(dragAndSize);
    const [dragAndSizeEnd, set_dragAndSizeEnd] = useState(dragAndSize);

    let userIsDragingOrDroping = ()=>{ 
        if(!trackToolTipMove && !trackToolTipSize){
            return(0)
        }else{
            return(100)
        }
    }

    /*------------------------- props/content -------------------------*/
    let move = props.move ?? `❖`;
    let size = props.size ?? ``
    let close = props.close ?? `❌`;
    
    let defaultStyleParent = {
        overflow : "hidden",
        zIndex : `${dragAndSizeEnd.zIndex}`,
        position: "absolute",
        left: `${dragAndSizeEnd.left}px`,
        top: `${dragAndSizeEnd.top}px`,
        width : `${dragAndSizeEnd.width}px`,
        height : `${dragAndSizeEnd.height}px`,
        display: "grid",
        gridTemplateRows: "auto 1fr auto",

    }
    let defaultStyleChild = {}
    let styleParent = {...props.styleParent, ... defaultStyleParent};
    let styleChild = {...props.styleChild, ... defaultStyleChild};
    let defaultCloseFunction = ()=>{console.log(`you got to do this part your self`)};
    // let closeFunction = props.closeFunction ?? defaultCloseFunction;


    if(dragAndSizeEnd.hidden == true){
        return(null);
    }else{

        return(
            <div
                onClick={()=>{
                    let temp_get_highest_index = get_highest_index();
                    let temp_dragAndSizeStart = {... dragAndSizeStart};
                    let temp_dragAndSizeEnd = {... dragAndSizeEnd};
                    temp_dragAndSizeStart.zIndex = temp_get_highest_index;
                    temp_dragAndSizeEnd.zIndex = temp_get_highest_index;
                }}
                style={{
                    zIndex : `${dragAndSizeEnd.zIndex}`,
                    position: "absolute",
                    left: "0px",
                    top: "0px",
                    width : `${userIsDragingOrDroping()}vw`,
                    height : `${userIsDragingOrDroping()}vh`,
                }}

                onMouseMove={(event)=>{
                    if(userIsDragingOrDroping()){
                        set_mouseUp({
                            x : event.clientX,
                            y : event.clientY,
                        })

                        // ToolTip
                        if(trackToolTipMove){
                            set_dragAndSizeEnd({
                                left: dragAndSizeStart.left + (mouseUp.x - mouseDown.x),
                                top: dragAndSizeStart.top + (mouseUp.y - mouseDown.y),
                                width : dragAndSizeStart.width,
                                height : dragAndSizeStart.height,
                            })
                        }
                        if(trackToolTipSize){
                            set_dragAndSizeEnd({
                                left: dragAndSizeStart.left,
                                top: dragAndSizeStart.top,
                                width : dragAndSizeStart.width + (mouseUp.x - mouseDown.x),
                                height : dragAndSizeStart.height + (mouseUp.y - mouseDown.y),
                            })
                        }
                    }
                }}
                onMouseUp={()=>{
                    set_trackMouse(false);

                    // ToolTip
                    set_trackToolTipMove(false)
                    set_trackToolTipSize(false)
                    set_dragAndSizeStart(dragAndSizeEnd);
                }}
            >
                <div
                    onClick={()=>{
                
                        let temp_get_highest_index = get_highest_index();
                        let temp_dragAndSizeStart = {... dragAndSizeStart};
                        let temp_dragAndSizeEnd = {... dragAndSizeEnd};
                        temp_dragAndSizeStart.zIndex = temp_get_highest_index;
                        temp_dragAndSizeEnd.zIndex = temp_get_highest_index;

                        set_dragAndSizeStart(temp_dragAndSizeStart);
                        set_dragAndSizeEnd(temp_dragAndSizeEnd);
                    }}
                    style={styleParent}
                >
                    <div
                        style={{
                            display: "grid",
                            gridTemplateColumns: "auto auto",
                        }}
                    >
                        <div
                            onMouseDown={(event)=>{

                                dragAndSizeStart.zIndex = get_highest_index();
                                set_dragAndSizeStart(dragAndSizeStart);
                                dragAndSizeEnd.zIndex = get_highest_index();
                                set_dragAndSizeEnd(dragAndSizeStart);

                                set_trackToolTipMove(true);
                                set_mouseDown({
                                    x : event.clientX,
                                    y : event.clientY,
                                })
                                set_mouseUp({
                                    x : event.clientX,
                                    y : event.clientY,
                                })
                            }}
                        >
                            {move}
                        </div>
                        <div
                            onMouseDown={(event)=>{
                                // THIS IS WHERE YOU CLOSE THING
                                
                                if(props.closeFunction == undefined){
                                    defaultCloseFunction()
                                }else{
                                    props.closeFunction();
                                }

                            }}
                            style={{
                                textAlign : "right"
                            }}
                        >
                            {close}
                        </div>
                    </div>

                        <div
                            style={styleChild}
                        >
                            {props.children}
                        </div>
                    <div
                        onMouseDown={(event)=>{

                            let temp_get_highest_index = get_highest_index();
                            let temp_dragAndSizeStart = {... dragAndSizeStart};
                            let temp_dragAndSizeEnd = {... dragAndSizeEnd};
                            temp_dragAndSizeStart.zIndex = temp_get_highest_index;
                            temp_dragAndSizeEnd.zIndex = temp_get_highest_index;

                            set_trackToolTipSize(true);
                            set_mouseDown({
                                x : event.clientX,
                                y : event.clientY,
                            })
                            set_mouseUp({
                                x : event.clientX,
                                y : event.clientY,
                            })
                        }}
                        style={{
                            textAlign: "right",
                        }}
                    >
                        {size}
                    </div>
                </div>
            </div>
        )
    }
}

when I am setting the closeFunction for DragAndSize I am getting an outdated copy. on the first div Im getting an empty array the second div gives me an array with one element in it. dose any one know who I can get the up to date parameter?

1 Answers

Quick fix (but probably bad solution):

Use a Ref:

const [toolTips, set_toolTips] = useState([]);
const toolipsRef = useRef(toolTips);

useEffect(() => {
  toolTips.current = toolTips;
}, [toolTips]);

...

closeFunction={ () => {
  console.log(toolipsRef.current)
} }

Also, note that your new state is based on the previous one, so you should be using functional updates, as shown in the example below.

Better alternative:

  • Instead of adding the rendered component to state, only add the props for the tooltips you want to render that do not change (closeFunction won't be in the state).

    If needed, you can add a tooltipType prop, which in this case will be "DragAndSize", but could be something different if you have other types of "tooltip" components

    <button
      onClick={() => {
        set_toolTips((...prevTooltips) => ([
          ...prevTooltips,
          {
            tooltipType: "DragAndSize"
            dragAndSize: {
              left: 20,
              top: 20,
              width : 400,
              height : 400,
              hidden : false,
              zIndex : 1000
            }
            styleParent: {
              backgroundColor: "blue",
              borderRadius: "10px",
              borderColor : "balck",
              borderWidth : "thick",
            },
          },
        ]);
      }>
      Add
    </button>
    
  • You then render these as you would normally do using toolTips.map(...) and adding the missing props (closeFunction in this case):

    toolTips.map(({ tooltipType, ...tooltipProps }, index) => {
      let tooltipElement = null;
    
      if (tooltipType === "DragAndSize") {
        tooltipElement = (
          <DragAndSize
            { ...tooltipProps }
            closeFunction={ () => console.log(toolTips) }/>
        );
      }
    
      return tooltipElement ? (
        <div key={index}>
          { tooltipElement }
        </div>
      ) : null;
    });
    
Related