React js:我可以将数据从组件传递到另一个组件吗?

时间:2017-07-15 15:20:28

标签: javascript ajax reactjs components react-jsx

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

让我解释一下我的问题:

我有一个名为<NewReleases />的组件,我在那里进行ajax调用,并在今天的电影院中播放一些关于电影的数据。 (我拿标题,海报img,概述等...)我把所有数据都放在<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')
);

(你在这里看不到<NewReleases />,因为它是在<Home />组件内部呈现的,它也会呈现页眉和页脚。)

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

我希望你得到我想要达到的目标。我不知道是否有可能。我有一个想法:在<Movie />我可以为我想要的电影做另一个ajax电话,但我觉得它会慢一些,而且我也不认为这对React来说是一个很好的解决方案。

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

3 个答案:

答案 0 :(得分:0)

你想做的事情更复杂。随着父母组件很容易做到。使用Redux更容易。

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

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

start_requests()

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

答案 1 :(得分:0)

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

当然,你必须重新设计它以匹配你的应用程序,但基本的故障是你的主组件应该是你正在抓取你的电影信息(通过AJAX或WS),然后是设置功能将允许您将所需的任何信息存储到父组件中,这最终将允许任何子组件访问您存储的信息。

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);
}
<div id="app"></div>

答案 2 :(得分:0)

创建手动object商店以get/set电影信息并使用它。而已。请尝试以下代码。那应该回答你所有的问题。点击任何新版本,它将重定向到电影信息屏幕,其中包含所有详细信息。如果您对总是刷新的新版本数据感到不满,则可能需要创建另一个商店,然后通过检查商店中存在的数据来get/set数据。

注意:使用商店并在浏览器网址中使用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>