Click here to Skip to main content
15,887,027 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a Laravel 10 project with React.

I want to increment the Basket props.myState by 1 after clicking on the "Add to basket" button. It works when I use the ProductsMy component in the States2 component like this:

<productsmy products3="{products3}" mystate="{myState}" setmystate="{setMyState}/">


But I want to render it in a {children} prop. Below one line.

{/*<productsmy products3="{products3}" mystate="{myState}" setmystate="{setMyState}/">*/}
            {children}


When I use {children}, I got this message if I click on that button: setMyState is not a function.

Uncaught TypeError: setMyState is not a function
    at handleToBasketClick (ProductsMy.jsx?t=1689842814849:23:5)
    at HTMLUnknownElement.callCallback2 (react-dom.development.js:4164:14)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16)
    at invokeGuardedCallback (react-dom.development.js:4277:31)
    at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:4291:25)
    at executeDispatch (react-dom.development.js:9041:3)
    at processDispatchQueueItemsInOrder (react-dom.development.js:9073:7)
    at processDispatchQueue (react-dom.development.js:9086:5)
    at dispatchEventsForPlugins (react-dom.development.js:9097:3)
    at react-dom.development.js:9288:12


I think with the children the component does not get this:
myState={myState} setMyState={setMyState}


How can I use it with {children}? The ProductsMy component is loaded into
{children}


If I uncomment the ProductsMy component it works.

What I have tried:

Here is the code:

States2 component

JavaScript
import {useState} from 'react';

function Basket(props) {
    return (
        <>
            <h1>In basket: {props.myState} DB</h1>
        </>
    );
}

export default function States2({children}) {
    const [myState, setMyState] = useState(0);

    return (
        <div className="App">
            <header className="App-header">
                <Basket myState={myState}/> {/* Show Basket komponent */}
            </header>
            {/*<ProductsMy products3={products3} myState={myState} setMyState={setMyState}/>*/}
            {children}
        </div>
    );
}


ProductsMy.jsx
JavaScript
import Button from "react-bootstrap/Button";
import States2 from "@/Pages/States2.jsx";

export default function ProductsMy({myState, setMyState}) {
    const handleToBasketClick = () => {
        setMyState(myState + 1);
    };

    const products3 = [
        {title: 'Product 1'},
        {title: 'Product 2'},
        {title: 'Product 3'},
        {title: 'Product 4'},
        {title: 'Product 5'},
    ]

    return (
        <States2>
            <div>
                {products3.map((product, index) => (
                    <div className="flex items-center" key={index}>
                        <div>{product.title}</div>
                        <Button onClick={handleToBasketClick}>Add to Basket</Button>
                    </div>
                ))}
            </div>
        </States2>
    )
}


And the backend route:
PHP
Route::get('/states2', function () {
    return Inertia::render('ProductsMy');
});
Posted
Updated 19-Jul-23 23:47pm
v3

1 solution

Laravel and React not one of my strong points. You need to explicitly pass the 'setMyState' function down from the parent component to your 'ProductsMy' component - Using a function in `setState` instead of an object[^]

Remove the '{children}' prop from your States2 component since it will not be used.
Pass the 'setMyState' function to your 'ProductsMy' component as a prop -

In your States2 component -
jsx
import { useState } from 'react';
import Basket from './Basket'; //You have to import the Basket component for it to work

export default function States2() {
  const [myState, setMyState] = useState(0);

  return (
    <div className="App">
      <header className="App-header">
        <Basket myState={myState} />
      </header>
      <ProductsMy myState={myState} setMyState={setMyState} /> {/* Pass the setMyState function as a prop */}
    </div>
  );
}


In your 'ProductsMy' component, use the 'setMyState' prop directly in the 'handleToBasketClick' function -
jsx
import Button from 'react-bootstrap/Button';

export default function ProductsMy({ myState, setMyState }) {
  const handleToBasketClick = () => {
    setMyState(myState + 1);
  };

  const products3 = [
    { title: 'Product 1' },
    { title: 'Product 2' },
    { title: 'Product 3' },
    { title: 'Product 4' },
    { title: 'Product 5' },
  ];

  return (
    <div>
      {products3.map((product, index) => (
        <div className="flex items-center" key={index}>
          <div>{product.title}</div>
          <Button onClick={handleToBasketClick}>Add to Basket</Button>
        </div>
      ))}
    </div>
  );
}


[EDIT/ADDITIONAL INFORMATION]
As the above returns a similar outcome for you when you uncomment
jsx
/*<ProductsMy products3={products3} myState={myState} setMyState={setMyState}/>*/
, you can use an alternative solution using 'React.cloneElement' - React.cloneElement() Function[^]

Remove the import of the 'ProductsMy' component from your States2 component, as it will be rendered through the '{children}' prop.
In your 'States2' component, use 'React.cloneElement' to pass the 'setMyState' function to your 'ProductsMy' component when rendering it through the '{children}' prop -

States2 component -
jsx
import React, { useState, useEffect } from 'react';
import Basket from './Basket'; // Make sure to import the Basket component correctly

export default function States2({ children }) {
  const [myState, setMyState] = useState(0);

  useEffect(() => {
    // Function to pass down to the child components
    const updateState = (newState) => {
      setMyState(newState);
    };

    // Clone the children and pass the updateState function to them
    const clonedChildren = React.Children.map(children, (child) => {
      return React.cloneElement(child, { myState, updateState });
    });

    return () => {}; // Empty cleanup function
  }, [children]);

  return (
    <div className="App">
      <header className="App-header">
        <Basket myState={myState} />
      </header>
      {children}
    </div>
  );
}


ProductsMy -
jsx
import React from 'react';
import Button from 'react-bootstrap/Button';

export default function ProductsMy({ myState, updateState }) {
  const handleToBasketClick = () => {
    updateState(myState + 1);
  };

  const products3 = [
    { title: 'Product 1' },
    { title: 'Product 2' },
    { title: 'Product 3' },
    { title: 'Product 4' },
    { title: 'Product 5' },
  ];

  return (
    <div>
      {products3.map((product, index) => (
        <div className="flex items-center" key={index}>
          <div>{product.title}</div>
          <Button onClick={handleToBasketClick}>Add to Basket</Button>
        </div>
      ))}
    </div>
  );
}


Maybe not exactly what you require but you can tweak this to work for you.
 
Share this answer
 
v4
Comments
folza 20-Jul-23 6:43am    
I want to use the ProductsMy render to the {children} because there will be other components instead of that. Your solution is what I said if I uncomment the ProductsMy component in the States2 component. And that is not a good solution for me. Please delete your solution.
Andre Oosthuizen 20-Jul-23 7:54am    
See my updated solution.
folza 20-Jul-23 8:04am    
The same error setMyState is not a function. For something the rendering to the {children} does not get the following functions like in this line:
<productsmy products3="{products3}" mystate="{myState}" setmystate="{setMyState}/"> Do you know a proficient React developer who can help?
Andre Oosthuizen 20-Jul-23 8:28am    
It looks like the 'setMyState' prop is not being passed down to the 'ProductsMy' component correctly. See my updated code.

Unfortunately I do not know a dev that can assist, you will find many on a pay site like Freelancer etc.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900