【问题标题】:React js: can I pass data from a component to another component?React js:我可以将数据从一个组件传递到另一个组件吗?
【发布时间】:2017-07-15 15:20:28
【问题描述】:

我是 React 新手,我还在学习它。我正在用它做一个个人项目。

让我解释一下我的问题:

我有一个名为 <NewReleases /> 的组件,我在其中进行了 ajax 调用并获取了一些关于今天在电影院上映的一些电影的数据。 (我取标题、海报图片、概述等...)我将所有数据放在<NewReleases /> 状态中,这样状态就变成了一个对象,其中包含每部电影的对象,每个对象都包含标题属性、海报属性等...然后我渲染组件,使它看起来像一个由电影海报、信息等组成的网格。这很好用。

然后我需要一个组件<Movie /> 来从<NewReleases /> 的状态中获取一些数据并将它们呈现在HTML 上。我阅读了人们遇到类似问题的其他问题,但情况不同,因为他们有一个由父组件呈现的子组件。这样,人们建议将状态作为道具传递。我不能这样做,因为我的<Movie /> 组件不是由<NewReleases /> 呈现的。 <NewReleases /> 进行 ajax 调用并仅根据检索到的数据呈现 JSX 网格。

index.js 我已经这样设置了主页:

import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Switch, Route} from 'react-router-dom';

import {Home} from './home';
import {Movie} from './movie';
import './css/index.css';

class App extends React.Component {
  render() {
    return(
      <BrowserRouter>
        <Switch>
          <Route path={'/movie/:movieTitle'} component={Movie} />
          <Route path={'/'} component={Home} />
        </Switch>
      </BrowserRouter>
    );
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

(您在这里看不到&lt;NewReleases /&gt;,因为它在&lt;Home /&gt; 组件内部呈现,该组件还呈现页眉和页脚。)

所以当我点击&lt;NewReleases /&gt; 渲染的电影时,应用程序会让我继续localhost:3000/movie/:movieTitle 其中 :movieTitle 是一种动态的方式来表示电影的标题(例如,如果我点击星球大战由&lt;NewReleases /&gt; 渲染,我会继续localhost:3000/movie/StarWars)。在该页面上,我想显示有关该电影的详细信息。信息存储在&lt;NewReleases /&gt; 状态,但我无法从&lt;Movie /&gt; 访问该状态(我猜)。

我希望你得到了我想要实现的目标。我不知道这是否可能。我有一个想法:在&lt;Movie /&gt; 上,我可以为我想要的电影再做一次 ajax 调用,但我认为它会更慢,而且我认为这不是 React 的一个好的解决方案。

请注意,我没有使用 Redux、Flux 等……只有 React。我想在转向其他技术之前很好地了解 React。

【问题讨论】:

  • 如果您希望您的数据在全球范围内可用,请在客户端使用 localStorage 或在服务器端使用 Database。 (保持简单)
  • 不,你不能。您将不得不使用一些肮脏的技巧。这就是我讨厌反应的原因。

标签: javascript ajax reactjs components react-jsx


【解决方案1】:

你想要做的方式更复杂。使用父母组件,这很容易做到。并且使用 Redux 更容易。

但是,你想这样。我认为如果您在应用程序中有一个状态,请将一个道具传递给 home 以设置一个电影状态并将这个电影状态传递给组件 Move,工作正常。

问题是 Route 没有传递 props。所以你可以做一条可扩展的路线。在下面的代码中,我从网上获得了这个 PropsRoute。

import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter, Switch, Route} from 'react-router-dom';

import {Home} from './home';
import {Movie} from './movie';
import './css/index.css';

    class App extends React.Component {
      constructor() {
        super();

        this.state = {
          movie: {}
        }

        this.setMovie = this.setMovie.bind(this);
      }

      setMovie(newMovie) {
        this.setState({
          movie: newMovie
        });
      }

      render() {
        return(
          <BrowserRouter>
            <Switch>
              <PropsRoute path={'/movie/:movieTitle'} movie={this.state.movie} component={Movie} />
              <PropsRoute path={'/'} component={Home} setMovie={this.setMovie} />
            </Switch>
          </BrowserRouter>
        );
      }
    }

    ReactDOM.render(
      <App />,
      document.getElementById('root')
    );


-----
const renderMergedProps = (component, ...rest) => {
  const finalProps = Object.assign({}, ...rest);
  return (
    React.createElement(component, finalProps)
  );
}

const PropsRoute = ({ component, ...rest }) => {
  return (
    <Route {...rest} render={routeProps => {
      return renderMergedProps(component, routeProps, rest);
    }}/>
  );
}

我认为这可以解决您的问题。

【讨论】:

    【解决方案2】:

    这是一个简单的例子。将您的状态存储在父组件中,并将状态向下传递给您的组件。此示例使用 React Router 4,但它显示了如何通过状态将 setMovie 函数和电影信息传递给您的子组件之一。 https://codepen.io/w7sang/pen/owVrxW?editors=1111

    当然,您必须重新设计它以匹配您的应用程序,但一个基本的问题是您的 home 组件应该是您获取电影信息的位置(通过 AJAX 或 WS),然后是 set 函数将允许您将所需的任何信息存储到父组件中,最终将允许任何子组件访问您存储的信息。

    const {
      BrowserRouter, 
      Link, 
      Route,
      Switch
    } = ReactRouterDOM;
    const Router = BrowserRouter;
    
    // App
    class App extends React.Component{
      constructor(props) {
        super(props);
        this.state = {
          movie: {
            title: null,
            rating: null
          }
        };
        this.setMovie = this.setMovie.bind(this);
      }
      setMovie(payload) {
        this.setState({
          movie: {
            title: payload.title,
            rating: payload.rating
          }
        });
      }
      render(){
        return(
        <Router>
          <div className="container">
            <Layout>
              <Switch>
                <Route path="/select-movie" component={ () => <Home set={this.setMovie} movie={this.state.movie} />}  /> 
                <Route path="/movie-info" component={()=><MovieInfo movie={this.state.movie}/>} /> 
              </Switch>
            </Layout>
          </div>
        </Router>
        )
      }
    }
    
    //Layout
    const Layout = ({children}) => (
      <div>
        <header>
          <h1>Movie App</h1>
        </header>
        <nav>
          <Link to="/select-movie">Select Movie</Link>
          <Link to="/movie-info">Movie Info</Link>
        </nav>
        <section>
          {children}
        </section>
      </div>
    )
    
    //Home Component
    const Home = ({set, movie}) => (
      <div>
        <Movie title="Star Wars VIII: The Last Jedi (2017)" rating={5} set={set} selected={movie} />
        <Movie title="Star Wars VII: The Force Awakens (2015)" rating={5} set={set} selected={movie} />
      </div>
    )
    
    //Movie Component for displaying movies
    //User can select the movie
    const Movie = ({title, rating, set, selected}) => {
      const selectMovie = () => {
        set({
          title: title,
          rating: rating
        });
      }
      return (
        <div className={selected.title === title ? 'active' : ''}>
          <h1>{title}</h1>
          <div>
            {Array(rating).fill(1).map(() =>
            <span>★</span>
            )}
          </div>
          <button onClick={selectMovie}>Select</button>
        </div>
      )
    }
    
    //Movie Info
    //You must select a movie before movie information is shown
    const MovieInfo = ({movie}) => {
      
      const {
        title,
        rating
      } = movie;
      
      //No Movie is selected
      if ( movie.title === null ) {
        return <div>Please Select a Movie</div>
      }
      
      //Movie has been selected
      return (
        <div>
          <h1>Selected Movie</h1>
          {title}
          {Array(rating).fill(1).map(() =>
            <span>★</span>
          )}
        </div>
      )
      
    }
    
    ReactDOM.render(<App />,document.getElementById('app'));
    nav {
      margin: 20px 0;
    }
    
    a {
      border: 1px solid black;
      margin-right: 10px;
      padding: 10px;
    }
    
    .active {
      background: rgba(0,0,0,0.2);
    }
    &lt;div id="app"&gt;&lt;/div&gt;

    【讨论】:

      【解决方案3】:

      创建手动object 存储到get/set 电影信息并使用它。就是这样。试试下面的代码。那应该回答你所有的问题。单击任何新版本,它将重定向到包含所有详细信息的电影信息屏幕。如果您对新发布的数据总是刷新感到不快,您可能需要创建另一个存储,然后通过检查存储中的数据来get/set 数据。

      注意: 在浏览器 URL 中使用 store 和使用title(可能会出现重复)会在用户刷新浏览器时出现一些问题。为此,请在浏览器 URL 中使用 id,使用 AJAX 调用获取详细信息并在存储中设置该详细信息。

      //store for movie info
      const movieInfoStore = {
        data: null,
        set: function(data) {
          this.data = data;
        },
        clear: function() {
          this.data = null;
        }
      };
      
      class MovieInfo extends React.Component {
        componentWillUnmount() {
          movieInfoStore.clear();
        }
        
        render() {
          return (
            <div>
              <pre>
                {movieInfoStore.data && JSON.stringify(movieInfoStore.data)}
              </pre>
              <button onClick={() => this.props.history.goBack()}>Go Back</button>
            </div>
          )
        }
      }
      
      MovieInfo = ReactRouterDOM.withRouter(MovieInfo);
      
      class NewReleases extends React.Component {
        handleNewReleaseClick(newRelease) {
          movieInfoStore.set(newRelease);
          this.props.history.push(`/movie/${newRelease.title}`);
        }
        
        render() {
          const { data, loading } = this.props;
          
          if(loading) return <b>Loading...</b>;
          
          if(!data || data.length === 0) return null;
          
          return (
            <ul>
              {
                data.map(newRelease => {
                  return (
                    <li onClick={() => this.handleNewReleaseClick(newRelease)}>{newRelease.title}</li>
                  )
                })
              }
            </ul>
          )
        }
      }
      
      NewReleases = ReactRouterDOM.withRouter(NewReleases);
      
      class Home extends React.Component {
        constructor(props) {
          super(props);
          
          this.state = {
            newReleases: [],
            newReleasesLoading: true
          };
        }
      
        componentDidMount() {
          setTimeout(() => {
            this.setState({
              newReleases: [{id: 1, title: "Star Wars"}, {id: 2, title: "Avatar"}],
              newReleasesLoading: false
            });
          }, 1000);
        }
      
        render() {
          const { newReleases, newReleasesLoading } = this.state;
        
          return (
            <NewReleases data={newReleases} loading={newReleasesLoading} />
          )
        }
      }
      
      class App extends React.Component {
        render() {
          const { BrowserRouter, HashRouter, Switch, Route } = ReactRouterDOM;
        
          return (
            <HashRouter>
              <Switch>
                <Route path="/movie/:movieTitle" component={MovieInfo} />
                <Route path="/" component={Home} />
              </Switch>
            </HashRouter>
          )
        }
      }
      
      ReactDOM.render(<App />, document.getElementById("root"));
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
      <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
      <script src="https://unpkg.com/react-router-dom/umd/react-router-dom.min.js"></script>
      
      <div id="root"></div>

      【讨论】:

        猜你喜欢
        • 2021-04-16
        • 1970-01-01
        • 1970-01-01
        • 2019-08-03
        • 1970-01-01
        • 2022-08-17
        • 2021-09-14
        相关资源
        最近更新 更多