简体   繁体   中英

React - Access function parameters from parent to child

A bit of background...

I'm in the middle of creating an app in React (which is working great) but getting frustrated with the amount of state/functions I have to keep in my parent component.

What I want to do is push some of the state and functions to a sub-parent component, making it easier to manage.

One area of functionality I'm having issues with is a Modal component that needs to render within the parent component's JSX.

This Modal I have made reusable so it only renders {this.props.children} . The content is decided with a switch case statement and it displays a new component as it's content depending on its 'mode'

The Issue...

Not sure if there's a pattern in React for this but I'd like to pass a function, with its parameters to another child component via props, with the child setting the parameters as its state on render.

Here's a simplified version of the code, showing only 1 modal 'mode/content option':

import React, { Component } from "react";
import ReactDOM from "react-dom";

class Items extends Component {
    state = {
        categories: [
            {
                id: 1,
                name: "category 1",
                items: [
                    { name: "item 1", id: Math.floor(Math.random() * 99999) },
                    { name: "item 2", id: Math.floor(Math.random() * 99999) }
                ]
            },
            {
                id: 2,
                name: "category 2",
                items: [
                    { name: "item 3", id: Math.floor(Math.random() * 99999) },
                    { name: "item 4", id: Math.floor(Math.random() * 99999) }
                ]
            },
            {
                id: 3,
                name: "category 3",
                items: [
                    { name: "item 5", id: Math.floor(Math.random() * 99999) }
                ]
            }
        ],
        modalOpen: true,
        modalMode: ""
    };

    getClickedItem(item, category) {
        console.log("[click from parent component]", arguments);
        return arguments
    };

    openModal(mode, item, category) {
        if (mode === "editItem") {
            this.getClickedItem(item, category);
        }
        this.setState({
            modalOpen: true,
            modalMode: mode
        });
    }

    closeModal() {
        this.setState({
            modalOpen: false,
            modalMode: ""
        });
    }
    render() {
        const { categories, modalOpen, modalMode } = this.state;

        let modalContent;

        switch (modalMode) {
            case "editItem":
                modalContent = (
                    <Component1 closeModal={this.closeModal.bind(this)} getClickedItem={this.getClickedItem}/>
                );
                break;
            default:
                break;
        }

        return (
            <div>
                {categories.map(cat => {
                    return (
                        <ItemCategory
                            {...cat}
                            key={cat.id}
                            click={this.openModal.bind(this)}
                        />
                    );
                })}
                <Modal show={modalOpen}>{modalContent}</Modal>
            </div>
        );
    }
}

class ItemCategory extends Component {
    handleClick(item, category) {
        this.props.click("editItem", item, category);
    }
    render() {
        const { items, name } = this.props;

        const getItems = items.map(item => {
            return item;
        });

        return (
            <div>
                <div>-{name}</div>
                <ul>
                    {getItems.map(item => {
                        return (
                            <li
                                key={item.id}
                                onClick={() => this.handleClick(item, name)}
                            >
                                {item.name}
                            </li>
                        );
                    })}
                </ul>
            </div>
        );
    }
}

class Component1 extends Component {
    state = {
        item: "",
        category: ""
    }

    static getDerivedStateFromProps(props, state) {
        const getParams = props.getClickedItem()
        console.log("[from child component]", getParams)
    }

    render() {
        const { item, category } = this.state;
        const { closeModal } = this.props;

        return (
            <div>
                <h1>Component1 modal content</h1>
                <button onClick={closeModal}>Close</button>
                {item}
                {category}
            </div>
        );
    }
}

class Modal extends Component {
    render() {
        return (
            <div
                style={{
                    transform: this.props.show
                        ? "translateY(0)"
                        : "translateY(-100vh)",
                    opacity: this.props.show ? "1" : "0"
                }}
            >
                {this.props.children}
            </div>
        );
    }
}

function App() {
    return <Items />;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Also available as a codepen https://codesandbox.io/s/m7jm5z0w4j

The function seems to pass to the child component fine, I think! But I can't consume the parameters from the parent component.

Any insight into this would be great, if you need anymore details just let me know.

Thanks :)

is the problem is with openModal and closeModal?

define the functions like this:

openModal = () => {
    this.setState({
        modalOpen: true
    });
}

then pass it as a prop like this

click={() => this.openModal()}

Explanation

The way I wrote the function is effectively equivalent to writing the function like this

constructor() {
    this.openModal = this.openModal.bind(this)
}

openModal() {
    this.setState({modalOpen: true}) 
} 

the difference is that I used a fat arrow function.

You can call the function like this this.openModal()

Then you have to pass the function as a prop. If you pass it as this.openModal , the child component can't access parent state, but if you pass it as () => this.openModal() , the effect of the function is computed before it is passed, this should work. This way of passing functions works in Angular as well.

Update

In this function

getClickedItem(item, category) {
    console.log("[click from parent component]", arguments);
    return arguments
};

arguments isn't defined.

Also, your openModal function accepts three arguements but you pass none.

actually your code doen't look like it is going to work as is.... but ye, pass function props as fat arrow functions


working - I pretty significantly changed your code

import React, { Component } from "react";
import ReactDOM from "react-dom";

class Items extends Component {
  state = {
    categories: [
      {
        id: 1,
        name: "category 1",
        items: [
          { name: "item 1", id: Math.floor(Math.random() * 99999) },
          { name: "item 2", id: Math.floor(Math.random() * 99999) }
        ]
      },
      {
        id: 2,
        name: "category 2",
        items: [
          { name: "item 3", id: Math.floor(Math.random() * 99999) },
          { name: "item 4", id: Math.floor(Math.random() * 99999) }
        ]
      },
      {
        id: 3,
        name: "category 3",
        items: [{ name: "item 5", id: Math.floor(Math.random() * 99999) }]
      }
    ],
    modalOpen: false,
    selectedItem: {}
  };
  openModal = item => {
    this.setState({
      modalOpen: true,
      selectedItem: item
    });
  };

  closeModal = () => {
    this.setState({
      modalOpen: false
    });
  };
  render() {
    const { categories, modalOpen, selectedItem } = this.state;
    return (
      <div>
        {categories.map(cat => {
          return (
            <ItemCategory
              {...cat}
              key={cat.id}
              click={item => this.openModal(item)}
            />
          );
        })}
        <Modal show={modalOpen}>
          <Component1
            closeModal={() => this.closeModal()}
            item={selectedItem}
          />
        </Modal>
      </div>
    );
  }
}

class ItemCategory extends Component {
  handleClick = item => {
    this.props.click(item);
  };
  render() {
    const { items, name } = this.props;

    const getItems = items.map(item => {
      return item;
    });

    return (
  <div>
    <div>-{name}</div>
    <ul>
      {getItems.map(item => {
        return (
          <li key={item.id} onClick={() => this.handleClick(item)}>
            {item.name}
          </li>
        );
      })}
    </ul>
  </div>
);
  }
}

class Component1 extends Component {
  render() {
    const { item, closeModal } = this.props;
    console.log(item);
    return (
      <div>
        <h1>{item.name} modal content</h1>
        <button onClick={closeModal}>Close</button>
      </div>
    );
  }
}

class Modal extends Component {
  render() {
    return (
      <div
        style={{
          transform: this.props.show ? "translateY(0)" : "translateY(-100vh)",
          opacity: this.props.show ? "1" : "0"
        }}
      >
        {this.props.children}
      </div>
    );
  }
}

function App() {
  return <Items />;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM