Home > Software design >  JavaScript React: Issues with passing prop from a function in a child component to a function in the
JavaScript React: Issues with passing prop from a function in a child component to a function in the

Time:12-20

I'm dealing with a problem passing a prop to a parent component from it's child.

The idea of the code that I'm trying to make work is a set of buttons in a header component that when clicked, load new component pages for other parts of the website. I'm dealing with a couple of smaller bugs that I can fix at another time but the primary issue is when I try to pass the results of the function for handling the switch and the values showing as 'undefined' once they get to the App component. I doubt I'm explaining it well so allow me to show the code.

Parent Component (App)

import React from "react";
import Header from "./Components/Header/Header";
import Footer from "./Components/Footer/Footer";
import Pane from "./Components/Pane/Pane";
import MainPane from "./Components/Pane/MainPane";
import BookViewPane from "./Components/Pane/BookViewPane";
import AddBookPane from "./Components/Pane/AddBookPane";
import SignInPane from  "./Components/Pane/SignInPane";
import "./App.css";

const App = ()=>{

    function LoadPaneHandler(props){
        var NewPaneName=props.paneName;

        // const NewPaneName={
        //  name: props.paneName
        // };
        
        // const NewPaneName=String(props);

        console.log(NewPaneName);
        switch(NewPaneName){
            case 'MainPane':
                return <MainPane />
            case 'AddBookPane':
                return <AddBookPane />
            case 'BookViewPane':
                return <BookViewPane />
            case 'SignInPane':
                return <SignInPane />
            default:
                return <Pane />
        }
    }

    return(
        <React.Fragment>
            <Header switchPane={LoadPaneHandler} />
            <main>
                <LoadPaneHandler paneName="MainPane" />
            </main>
            <Footer />
        </React.Fragment>
    );
}

export default App;

Child Component (Header)

import React from "react";
import "./Header.css";

const Header=(props)=>{
    var paneName="";

    const switchPaneHandler=event=>{
        event.preventDefault();
        console.log(paneName);
        props.switchPane(paneName);
    }

    return(
        <header id="header">
            <div id="header-title">
                <h1>Library</h1>
            </div>
            <div id="header-buttons">
                <button onClick={paneName="BookViewPane",switchPaneHandler}> View Books</button>
                <button onClick={paneName="AddBookPane",switchPaneHandler}> Add Books </button>
                <button onClick={paneName="SignInPane",switchPaneHandler}> Login</button>
            </div>
        </header>
    );
}

export default Header;

I've included the commented out code of other approaches I've used to get the data I need for the function to work properly so that you can have an Idea of what I've already tried.

The code works fine so long as I only pass values to the function from within the App component. Whenever I click on one of the buttons in the header though, it shows the 'paneName' correctly in the 'switchPaneHandler' function but then in 'LoadPaneHandler' it prints as 'undefined'.

I'm still quite new to React so it's likely a very obvious mistake that I've made but any help is appreciated all the same. Thanks!

CodePudding user response:

Try to change your child component (Header) like this.

import React from "react";
import "./Header.css";
const Header = (props) => {

    const switchPaneHandler = paneName => {
        console.log(paneName);
        props.switchPane(paneName);
    }

    return(
        <header id="header">
            <div id="header-title">
                <h1>Library</h1>
            </div>
            <div id="header-buttons">
                <button onClick={()=> switchPaneHandler('BookViewPane')}> View Books</button>
                <button onClick={() => switchPaneHandler("AddBookPane")}> Add Books </button>
                <button onClick={() => switchPaneHandler("SignInPane")}> Login</button>
            </div>
        </header>
    );
}

CodePudding user response:

In header component your passing the paneName directly, but in parent component you are trying to get as javascript object (props.paneName), that is why you are getting error.

const switchPaneHandler=event=>{
    event.preventDefault();
    console.log(paneName);
    props.switchPane(paneName);
}

so try to access directly,

function LoadPaneHandler(paneName){
    var NewPaneName = paneName;

    console.log(NewPaneName);
    switch(NewPaneName){
        case 'MainPane':
            return <MainPane />
        case 'AddBookPane':
            return <AddBookPane />
        case 'BookViewPane':
            return <BookViewPane />
        case 'SignInPane':
            return <SignInPane />
        default:
            return <Pane />
    }
}

CodePudding user response:

I think the key issue here is probably caused by confusion about "What are props? What is state?" - very common when getting started with React.

If we look at the parent component first, you're passing LoadPaneHandler to your Header like it's a callback function. That's not how we do it in React. We need to supply a callback function that takes the name of the pane that we want the parent to show. Naming can really help too in order to make things clearer. Here's how I'd rewrite your parent:

const App = ()=>{
    const [currentPaneName, setCurrentPaneName] = React.useState("MainPane")
    
    function updateCurrentPane(newPaneName) {
      console.log(`Updating pane from ${currentPaneName} to ${newPaneName}`);
      setCurrentPaneName(newPaneName)
    }

    function LoadPaneHandler(){

        console.log(`Showing pane ${currentPaneName}`);
        switch(currentPaneName){
            case 'MainPane':
                return <MainPane />
            case 'AddBookPane':
                return <AddBookPane />
            case 'BookViewPane':
                return <BookViewPane />
            case 'SignInPane':
                return <SignInPane />
            default:
                return <Pane />
        }
    }

    return(
        <React.Fragment>
            <Header onNewPaneSelected={updateCurrentPane} />
            <main>
                <LoadPaneHandler />
            </main>
            <Footer />
        </React.Fragment>
    );
}

I've left a sprinkling of console.logs in there so you can get a feel for the timing of re-rendering and responding (React-ing) to change. If you're not familiar with useState(), this is the one React hook you absolutely must understand if you're going to build useful React applications - here are the docs.

Now for the child. You've got a paneName variable but you don't need it. All you're really trying to do is set up the right argument when you call the callback:

const Header=(props)=>{

    const switchPaneHandler= (paneName) =>{
        event.preventDefault();
        console.log(paneName);
        props. onNewPaneSelected(paneName);
    }

    return(
        <header id="header">
            <div id="header-title">
                <h1>Library</h1>
            </div>
            <div id="header-buttons">
                <button onClick={() => switchPaneHandler("BookViewPane")}> View Books</button>
                <button onClick={() => switchPaneHandler("AddBookPane")}> Add Books </button>
                <button onClick={() => switchPaneHandler("SignInPane")}> Login</button>
            </div>
        </header>
    );
}

Note I changed the name of the callback function it's expecting to be given as a prop - switchPane was a bit misleading - it's not this component's job to switch panes, it just needs to be able to tell someone that the user wants to switch. This also makes it easier to make changes in the future, for example if you have other things that are interested in which pane the user wants to see.

  • Related