简体   繁体   English

反应:如何从子组件渲染父组件?

[英]React: How to render a parent component from child component?

I've been tasked with building out a project for a client that entails one parent component wrapping three sub components. 我的任务是为一个客户构建一个项目,该项目需要一个父组件包装三个子组件。 Specifically the parent component renders the sub components into being according to the following manner: 具体来说,父组件根据以下方式将子组件呈现为:

PARENT = TOP + MIDDLE + BOTTOM 父母=顶+中+底

The PARENT component represents one of a group of items. 父组件代表一组项目中的一个。

In the TOP component there is a menu button that displays a list of all items in the group with the intent that should the user click on one of those links, the selected item will display (PARENT will "re-render to" the new item - there's logic in PARENT that will cause the selected, second, item to appear rather than the first). 在TOP组件中,有一个菜单按钮,该菜单按钮显示该组中所有项目的列表,其目的是用户单击这些链接之一,所选项目将显示(PARENT将“重新渲染”到新项目-PARENT中存在一种逻辑,该逻辑将导致所选择的第二项而不是第一项出现。

I'm speaking loosely here and can supply code if what I'm talking about it isn't clear (I think it should be). 我在这里讲的很松散,如果不清楚,我可以提供代码(我认为应该这样)。

The primary issue I'm having is trying to "re-render" the parent component from one of the child components. 我遇到的主要问题是尝试从子组件之一“重新渲染”父组件。 Essentially, there's a menu button that appears displaying a list of related items. 本质上,有一个菜单按钮出现,显示相关项目的列表。 Clicking on one of those items should re-render the parent component but this time displaying the selected item. 单击这些项目之一应重新呈现父组件,但这次显示所选项目。 I don't know of a way to do this however and was hoping to get some advice or help. 我不知道如何做到这一点,并希望得到一些建议或帮助。 I've spent the morning researching the topic and trying a few suggested ways to no avail. 我花了整个上午研究这个主题,并尝试了一些建议的方法,但没有成功。

Perhaps the approach I've taken so far is not the best to accomplish this sort of task. 到目前为止,也许我采取的方法并不是完成此类任务的最佳方法。 In any event, any assistance would be very helpful. 无论如何,任何帮助都是非常有帮助的。 Thank you! 谢谢!


EDIT: NOW WORKING 编辑:现在工作

EDIT #2: Since this has gotten quite a few views I just wanted to clarify that this was a proof of concept and also very simplified prototype/early version (no fancy transpilation, etc. - a can this idea work sort of thing ... since we weren't sure at the time). 编辑#2:由于已经获得了很多意见,我只是想澄清一下,这是一个概念证明,并且也是非常简化的原型/早期版本(没有花哨的翻译等)-这个想法可以起作用。 ,因为我们当时不确定。 It's been a while since I worked on anything like that but I really appreciate all the help for such a confusing question that I asked and what was at that time a very interesting and challenging task. 自从我从事类似工作以来已经有一段时间了,但是我真的很感谢我为这样一个令人困惑的问题所提供的所有帮助,以及当时那是一件非常有趣且具有挑战性的任务。

 import React from 'react';

import CardHeader from 'components/CardHeader';
import CardContent from 'components/CardContent';
import CardFooter from 'components/CardFooter';

module.exports = React.createClass({

  getInitialState: function () {
    return {
      fullData: '',

      //Core Card
      userId: '',
      cardStack: '',
      cardId: '',

      //Load Card
      loadCard: '1',

      //Optional Fields
      name: '',
      type: '',
      headline: '',
      subtitle: '',
      ctext: '',
      imageUrl: '',
      price: '',
      mapCoordinates: '',
      logoUrl: '',
      order: '',
      email: '',
      sponsorUrl: '',
      pollId: '',
      socialButton: ''
    };
  }
  ,

  componentWillMount: function () {

    //fetches cardStack and card API JSON
    /** JSON Structure:
     [
     cardStack: ...,
     cards: ...
     ]
     **/

    var fetch = function (userId, cardStackId) {

      //AJAX

      var json = {
        'cardStack': {'id': '1', 'name': 'Test Card Stack', 'userID': 'O1AB0001'},
        'cards': [{
          'id': '1',
          'name': 'Test Card 1',
          'cardStack': '1',
          'meta': 'meta_note',
          'socialButton': 'twitter',
          'sponsorUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
          'order': 1,
          'logoUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
          'product': {
            'headline': 'Headline Test',
            'subtitle': 'Subtitle Test',
            'ctext': 'Hello!!!!',
            'imageUrl': 'http://the-mpas.com/wp-content/uploads/2012/04/Image-pic-54-copy.jpg',
            'price': '20.00'
          }
        }, {
          'id': '2',
          'name': 'Test Card 2',
          'cardStack': '1',
          'meta': 'meta_note',
          'socialButton': 'twitter',
          'sponsorUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
          'order': 2,
          'logoUrl': 'https://iwantmyname.com/images/logo-url-shortener-droplr.png',
          'product': {
            'headline': 'Headline Test 2',
            'subtitle': 'Subtitle Test 2',
            'ctext': 'Hello 2!!!!',
            'imageUrl': 'http://the-mpas.com/wp-content/uploads/2012/04/Image-pic-54-copy.jpg',
            'price': '30.00'
          }
        }]
      };

      return json;
    };

    var that = this;

    var getCard = function (cardArray, cardOrder) {
      var card;
      for (var key in cardArray) {
        if (cardArray[key].order == cardOrder) {
          card = cardArray[key];
        }
      }

      if ('product' in card) {
        that.setState({
          type: 'product',
          headline: card.product.headline,
          subtitle: card.product.subtitle,
          ctext: card.product.ctext,
          imageUrl: card.product.imageUrl,
          price: card.product.price
        })
      }
      if ('article' in card) {
        that.setState({
          type: 'article',
          headline: card.article.headline,
          ctext: card.article.ctext
        })
      }

      if ('map' in card) {
        that.setState({
          type: 'map',
          mapCoordinates: card.map.mapCoordinates
        })
      }

      if ('relatedvideo' in card) {
        that.setState({
          type: 'relatedvideo',
          headline: card.relatedvideo.headline,
          ctext: card.relatedvideo.ctext,
          imageUrl: card.relatedvideo.imageUrl
        })
      }

      if ('poll' in card) {
        that.setState({
          type: 'poll',
          headline: card.poll.headline,
          subtitle: card.poll.subtitle,
          pollId: card.poll.pollId
        })
      }

      if ('imagegallery' in card) {
        that.setState({
          type: 'imagegallery',
          headline: card.imagegallery.headline,
          ctext: card.imagegallery.ctext,
          imageUrl: card.imagegallery.imageUrl
        })
      }

      if ('profile' in card) {
        that.setState({
          type: 'profile',
          headline: card.profile.headline,
          ctext: card.profile.ctext,
          imageUrl: card.profile.imageUrl
        })
      }

      if ('newsletter' in card) {
        that.setState({
          type: 'newsletter',
          email: card.newsletter.email
        })
      }
      return card;
    };

//Entry Point HERE
    var userId = 'O1AB0001', cardStackId = 1;
    var json = fetch(userId, cardStackId);
    var myCard = getCard(json.cards, this.state.loadCard);

//Set core data
    this.setState({

      //fulldata
      fullData: json,

      //card stack
      userId: json.cardStack.name,
      cardStack: json.cardStack.id,

      //card
      cardId: myCard.id,
      socialButton: myCard.socialButton,
      order: myCard.order,
      sponsorUrl: myCard.sponsorUrl,
      logoUrl: myCard.logoUrl,
      meta: myCard.meta,
      name: myCard.name
    });
  },

  setNew: function (nextState) {

    var nsFullData = nextState.fullData;
    var nsCards = nsFullData.cards;
    var nsCardStack = nsFullData.cardStack;

    var that = this;
    var getCard = function (cardArray, cardOrder) {
      var card;
      for (var key in cardArray) {
        if (cardArray[key].order == cardOrder) {
          card = cardArray[key];
        }
      }

      if ('product' in card) {
        that.setState({
          type: 'product',
          headline: card.product.headline,
          subtitle: card.product.subtitle,
          ctext: card.product.ctext,
          imageUrl: card.product.imageUrl,
          price: card.product.price
        })
      }
      if ('article' in card) {
        that.setState({
          type: 'article',
          headline: card.article.headline,
          ctext: card.article.ctext
        })
      }

      if ('map' in card) {
        that.setState({
          type: 'map',
          mapCoordinates: card.map.mapCoordinates
        })
      }

      if ('relatedvideo' in card) {
        that.setState({
          type: 'relatedvideo',
          headline: card.relatedvideo.headline,
          ctext: card.relatedvideo.ctext,
          imageUrl: card.relatedvideo.imageUrl
        })
      }

      if ('poll' in card) {
        that.setState({
          type: 'poll',
          headline: card.poll.headline,
          subtitle: card.poll.subtitle,
          pollId: card.poll.pollId
        })
      }

      if ('imagegallery' in card) {
        that.setState({
          type: 'imagegallery',
          headline: card.imagegallery.headline,
          ctext: card.imagegallery.ctext,
          imageUrl: card.imagegallery.imageUrl
        })
      }

      if ('profile' in card) {
        that.setState({
          type: 'profile',
          headline: card.profile.headline,
          ctext: card.profile.ctext,
          imageUrl: card.profile.imageUrl
        })
      }

      if ('newsletter' in card) {
        that.setState({
          type: 'newsletter',
          email: card.newsletter.email
        })
      }
      return card;
    };

    var myCard = getCard(nsCards, this.state.loadCard);

    this.setState({

      //fulldata
      fullData: nsFullData,

      //card stack
      userId: nsCardStack.name,
      cardStack: nsCardStack.id,

      //card
      cardId: myCard.id,
      socialButton: myCard.socialButton,
      order: myCard.order,
      sponsorUrl: myCard.sponsorUrl,
      logoUrl: myCard.logoUrl,
      meta: myCard.meta,
      name: myCard.name
    });
  },

  componentWillUpdate: function (nextProps, nextState) {
    if (nextState.loadCard !== this.state.loadCard) {
      this.setNew(nextState);
    }
  },

  render: function () {

    return (
      <div className='sg-cardBase'>
        <div className='sg-cardHeaderSection'>
          <CardHeader setLoadCard={i => this.setState({loadCard: i})} data={this.state}/>
        </div>
        <div className='sg-cardContentSection'>
          <CardContent data={this.state}/>
        </div>
        <div className='sg-cardFooterSection'>
          <CardFooter data={this.state}/>
        </div>
      </div>
    );
  }
});

You need to pass a callback into the child component that modifies the state of the parent component. 您需要将回调传递给子组件,以修改父组件的状态。 The parent can then re-render based on that state. 然后,父级可以根据该状态重新渲染。 For example: 例如:

var Parent = React.createClass({
  getInitialState: function() {
    return { index: 1 };
  },
  render: function() {
    return <div>
        <Child setIndex={i => this.setState({index: i})}/>
        <p>{this.state.index}</p>
      </div>
  }
})

var Child = React.createClass({
  render: function() {
    <button onClick={() => this.props.setIndex(5)}/>
  }
});

In your case, the currently selected item should be stored in the state of your parent component, and a callback passed into your top component so that it can change the selected item. 在您的情况下,当前选定的项目应以父组件的状态存储,并且回调应传递到顶部组件,以便它可以更改选定的项目。

I assume that the top and bottom stays, and the middle changes according to the menu. 我假设顶部和底部保持不变,中间根据菜单进行更改。

The top gets a callback from the parent. 顶部从父级获取回调。 When an option is selected in the top, it invokes the callback changeCurrentView , and notifies it about the currentView . 在顶部选择一个选项时,它会调用回调changeCurrentView ,并通知它有关currentView The callback changeCurrentView sets the parent state, invoking the render method. 回调changeCurrentView设置父状态,调用render方法。

You can now change the render middle in the parent, but I suggest that the middle will re-render the different views. 您现在可以在父级中更改渲染中间,但是我建议中间将重新渲染不同的视图。 So middle gets the currentView as view from the parent, and it re-renders the desired view: 因此Middle将从父级获取currentView作为视图,然后重新呈现所需的视图:

 class Parent extends React.Component { constructor(props) { super(props); this.state = { currentView: 0 }; this.changeCurrentView = this.changeCurrentView.bind(this); } changeCurrentView(currentView) { this.setState({ currentView }); } render() { return ( <div> <Top changeView={ changeCurrentView } /> <Middle currentView = { this.state.currentView } /> <Bottom /> </div> ); } } const views = [ <View1 />, <View2 />, <View3 /> ]; const Middle = ({ currentView }) => ( <div>{ views[currentView]; </div> ); 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 React 中渲染和更新来自父组件的子元素? - How to render and update child elements coming from a parent component in React? 如何从父组件调用组件function并将其更改为state以在React中渲染子组件 - How to call component function from parent component and change it state to render child component in React 反应原生中呈现子组件而不是父组件 - Render child component and not parent component in react native 如何使用 react 和 typescript 在父组件中渲染子组件? - How to render a child component within parent component using react and typescript? 在React中单击子组件时如何呈现父组件? - How to render a parent component upon clicking a child component in React? 如何将道具从父组件渲染到子组件? - How to render props from parent component to child? React,如何将道具从父组件发送到子组件? (在渲染 function 之外使用这些道具) - React, How to send props from parent component to child component? (to use those props outside render function ) React:如何从父组件重新加载数据并在子组件更新时呈现? - React: How to reload data from parent component and render when child component do update? 如何从子组件重新渲染父组件 - How to re-render parent component from child component 如何从子组件触发父组件重新渲染 - How to trigger re-render in Parent Component from Child Component
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM