将道具传递到渲染功能之外的组件

时间:2020-02-23 03:32:25

标签: reactjs

我有一个Tracks组件,其中列出了歌曲和歌曲艺术家。艺术家名称是一个链接,该链接会将视图带到ArtistDetail组件。像这样:

https://imgur.com/a/SfDqONw

我希望这样,以便当用户单击艺术家姓名时,他们将被带到艺术家详细信息组件,该组件将显示“艺术家是{单击的艺术家的名字}”。但是,如您在上面的代码段中所见,艺术家的名字没有出现。当我在艺术家详细信息组件中进行一些安慰时,它说道具名称未定义

import React, { Component } from "react";
import SpotifyWebAPI from "spotify-web-api-js";
import { ArtistDetail } from "./ArtistDetail";
const spotifyApi = new SpotifyWebAPI();

export class Tracks extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentArtist: "lala"
    };
  }

  openArtist = name => {
    this.setState({
      currentArtist: name
    });
    const artist = <ArtistDetail name={currentArtist}/>
     //even with const artist = <ArtistDetail name="francis"/> it says undefined

    this.props.switchView("ADetail");//this function is in change of changing the view from component to component.

    return artist;// it does not seem to return the prop to the component
  };

  render() {
    const { currentArtist } = this.state;
    return (
      <div>
                  <TableCell>
                    <Link
                      onClick={() => {
                        this.openArtist(artists.name);
                      }}
                    >
                      {console.log("the current artist")}
                      {console.log(currentArtist)}
                      {artists.name}
                    </Link>
        </div>
      </div>
    );
  }
}

export default Tracks;

这是ArtistDetail.js组件

export const ArtistDetail = (props) => {
    console.log("the prop name")
    console.log(props.name);
    return (
        <div>
            <h1> The artist is </h1>
            <h1>{props.name}</h1>
        </div>
    )
}

我只是想要一种情况,在单击时,道具会传递给组件。 Onclick,艺术家详细信息应以艺术家的名字作为道具,然后switchView会将显示的组件从轨道切换到艺术家详细信息。 switchView完成其工作。但是这个名字似乎并没有出现在artistDetail上。从我阅读的内容来看,您似乎无法在render方法之外调用组件。但是如果不使用openArtist方法,我不知道如何将道具用于艺术家的细节

我尝试了在轨道内渲染方法:

{currentArtist != "lala" ? (<ArtistDetail name="francis"/>) && (this.props.switchView("ADetail"))

它仍然无法显示弗朗西斯

4 个答案:

答案 0 :(得分:1)

我不好,我错过了关于弗朗索瓦的评论。再往前看,这是...

ArtistDetail正在事件处理程序中创建。它被返回给onClick并消失了,因为不希望事件处理程序返回任何东西。

如果要更改特定元素,则必须在其现有位置更改其道具,或在其父元素中替换它。我强烈建议更改道具而不是创建新道具,因为创建新组件的规模很大。

如果数据在树中完全不同的位置,请考虑使用useReducer或全局状态管理器来管理状态,并分派从顶部向下流动的更新。这是React的核心原则:数据向下流动。

答案 1 :(得分:1)

React根据render中发生的情况更新DOM及其数据。它实际上并不关心组件的功能。它只关心您渲染的内容。在render之外创建的任何组件都永远不会放入跟踪的树中。

那么,如何更新渲染的组件?通过更新数据,导致重新呈现。

React旨在使数据在呈现时向下流动。父母可能会更新其孩子,但孩子不会更新父母,兄弟姐妹或除其自身及其孩子之外的任何其他事物。这就是让所有魔力发生在render()内部的原因。因为render专注于组件及其子组件的操作,所以只需要关心其状态和道具如何影响下游组件,而不必担心上游组件。

这对于呈现效果很好,但是如果下游组件允许用户在上游更改某些内容,会发生什么呢?

这就是消息传递的来源。当数据向下游流动时,消息向上游流动。这个想法是,渲染是自上而下的过程,而交互是自下而上的过程。

关键是在两种情况下,流量都是在树的树枝上向上和向下流动。数据流向孩子,消息流向父母。如果我们尝试将消息发送给姨妈和堂兄弟数十次,我们将很快失去对谁负责内容的跟踪。通过向上传递消息以向下传输数据,我们始终可以查找树以找到真理的来源。

有几种不同的消息传递方式。

  • 处理程序道具:这是访问父数据的最紧密耦合的方式。它涉及在父组件中创建一个函数以更新其状态,然后将该函数作为道具传递给子组件。子级使用更新后的信息对其进行调用,而父级则更新其状态。更新状态会通知React使用新状态重新渲染该父级(及其子级)。受控表单输入可以执行此操作。但是,处理多个级别的组件或复杂的数据结构不切实际。

  • useReducer() React 16.8引入了Hooks,它为功能组件添加了许多功能。 useReducer是一个特别有用的钩子,用于管理多个组件(例如表单数据)中更复杂的状态。 useReducer不会返回dispatch方法,该方法可以用不同的对象以不同的方式影响状态,而不是为要管理的每个状态下钻一个方法。 dispatch({type: 'foo', value:'bar'})可能会在此useReducer实例管理的状态下更改'foo'值,而dispatch({type: 'increment'})可能会更新同一状态对象的另一个成员中的计数器。通过将dispatch从一个孩子传到另一个孩子,您可以使用它来告诉状态从任何需要该能力的孩子改变,并且祖先组件将获得消息以更新其状态并重新呈现其自身及其后代。 / p>

  • 全局状态管理器,诸如Redux和MobX之类的软件包具有与useReducer类似的功能,但是规模更大。它们提供了在应用程序顶部创建全局状态的方法,而不是不必一直钻研调度功能到需要的地方,而是让您选择要发送消息和接收新数据的组件。他们还拥有庞大的插件生态系统,以扩展其功能。

对于您的描述,我可能会选择一个全球状态经理。查看您的GIF,看起来您将获得一个非常大的数据集,并且全局管理人员可以通过多种方法简化给定组件中需要了解的状态。另外,如果您的详细信息视图与列表视图位于完全不同的分支中,那么要想从列表的最深处开始一直向功能列表或功能useReducer一侧发送消息,将需要进行大量的钻研。全球经理人规避了这一点。

例如,如果您使用Redux,则状态看起来像

{
  currentArtist: null,
  currentView: 'tracks',
  artists: [
    {...artist1Data},
    {...artist2Data},
    ...
  ]
}

此状态将在应用程序的顶部定义,如果需要,每个组件都可以使用。

您将拥有一个称为 reducer 的函数,该函数接收消息并根据该消息的需要更新状态。然后,Redux会获取您的初始状态和化简器,并让您可以访问状态和分派方法,类似于useReducer所做的事情。

对于类,您可以将该组件包装到Redux的高阶组件中,而对于功能组件,则可以通过useSelector()和{ {1}}钩子。

因此,在此状态下,无需在props中传递useDispatch()方法,而是保留不同视图根源的顶级组件将访问Redux状态,请检查switchView成员,并显示该视图。当Track组件需要切换到演出者详细信息视图时,它将可以访问Redux的currentView方法,并向Redux分配 action ,可能与dispatch类似。减速器可识别“ switchView”操作类型,并将该顶级全局状态的dispatch({type: 'switchView', view: 'detail'})成员更新为“详细信息”。

由于我们已经连接了该视图切换组件,因此它知道Redux状态何时更改。它看到当前视图现在应该是“详细信息”,并且它重新渲染以显示该视图而不是“轨道”视图。

缺少的作品正在更新艺术家。这几乎与更新视图相同。在更新视图之前,我们将调度另一个操作,例如currentView。这次,reducer看到“ setCurrentArtist”,并且知道将dispatch({type: 'setCurrentArtist', artist: name})成员设置为currentArtist中传递的值。但是,在细节视图内部我们已经有一个现有的ArtistDetail组件,而不是创建一个新的ArtistDetail组件。当我们将其连接到Redux状态时,它可以识别currentArtist何时更改,然后在该全局状态值更改时重新呈现自己。

这是更改React应用对用户的显示方式的关键。要更新演示文稿,我们无法修改现有组件。渲染一旦发生,它就会被发送到野外,再也不会被看到。取而代之的是,我们通过更新当前组件中的道具和状态,或通过消息传递祖先组件中的数据更改来触发重新渲染。基本上,每次我们希望分支看起来不同时,我们都会通过更改数据来重新渲染整个分支。

这听起来效率极低,但是React被设计为可以很快地做到这一点。如果开始感到迟钝,它会为您提供一些缓存的方法,但是通常情况下,如果组件本身渲染起来很复杂,而不是因为组件的原始数量,就会发生这种情况。

答案 2 :(得分:1)

好吧,所以看您的Dashboard组件,似乎switchView来自Dashboard上方的组件。调用它时,我假设它会更新该父组件上的状态。组件上的状态更新使React可以重新调用该组件的render方法,从而重新呈现其自身及其子代。

问题是Dashboard渲染了适当的孩子,但没有为他们填充任何道具。我猜Home和Tracks的默认演示文稿不需要道具,但是ArtistDetail没有可用的内容。

当您进入Tracks时,您已经超出了Dashboard的能力,因此Dashboard不知道Tracks在做什么。在“轨道”下创建新的ArtistDetail将无济于事。您需要使当前艺术家可用于仪表板,以便将艺术家插入可以看到的ArtistDetail中。

执行此操作的一种方法是执行与更新当前视图相同的操作-创建一个函数来更新仪表板中的currentArtist状态。将该状态传递到Dashboard的ArtistDetail组件中。将功能向下传递到您的链接,然后通过该功能发送新的当前艺术家。发送新的演出者会更新仪表板的状态,并通过将新的currentArtist传递给ArtistDetail来重新呈现。

export const Dashboard = ({ currentView, switchView}) => {

  // Get the current artist from state and a setter to update the state.
  // We set the state to "" as an initial value.
  const [currentArtist, setCurrentArtist] = useState("");

  return (
    <div >
      {currentView == "Home" && <Home/>}
      {currentView == "Tracks" && <Tracks switchView={switchView} currentView={currentView} setCurrentArtist={setCurrentArtist}/>} // make "setCurrentArtist" available to Tracks
      {currentView == "SearchE" && <SearchE/>}
      {currentView == "Popular" && <Popular/>}
      {currentView == "ADetail" && <ArtistDetail name={currentArtist} />}//when i do switchView("ADetail") in tracks, it switches to the artist view
    </div>
  );
};

只要Tracks中的链接在调用this.props.setCurrentArtist之前调用this.props.switchViews,就会填充currentArtist状态并准备好进行切换。

这是我下面描述的处理程序道具模式的示例。

  1. 祖先传递了一个函数。
  2. 后代通过该函数向上传递消息。
  3. 祖先的状态已更改。
  4. React看到祖先的状态发生了变化,并调用了祖先的render函数。
  5. 祖先使用更新的数据渲染自己及其后代。

希望这有助于了解消息如何更新祖先数据以更新子级。

当然,现在您有两个要传递给应用程序的功能。想象一下,当您分散了许多功能时感觉如何!功能道具可用于一些功能,但在某些时候,我的另一个答案中描述的其他方法之一可能更易于管理。

答案 3 :(得分:0)

@Michael 当我尝试这个:

{currentArtist != "lala" ? (<ArtistDetail name ="francis" }/> ) : null} 

该道具被定义为弗朗西斯,但是当switchView起作用时,该道具突然变得不确定吗?为什么?因此,当我考虑这一点时,switchView逻辑将显示原始艺术家的详细信息。没什么在我的仪表板中:

export const Dashboard = ({ currentView, switchView}) => {
  return (
    <div >
      {currentView == "Home" && <Home/>}
      {currentView == "Tracks" && <Tracks switchView={switchView} currentView={currentView}/>}
      {currentView == "SearchE" && <SearchE/>}
      {currentView == "Popular" && <Popular/>}
      {currentView == "ADetail" && <ArtistDetail/>}//when i do switchView("ADetail") in tracks, it switches to the artist view
    </div>
  );
};

所以我想也许就是为什么它不起作用,即使更新了道具,上面的逻辑也只显示了原始的艺术家视图。但是,如果是这样的话,当我调用siwtchView(“ whatever page”)时,其他视图就不应仅显示页面的原始状态(即无显示)