React Js setState with params when onClick

心已入冬 提交于 2020-01-15 08:59:33

问题


I am using Reactjs to make a simple list of albums and after a click on each item I have to get all photos of this album, so I think to change the state (album_Id) .. the problem is I can't change the state and I dont know how to load the second page (to display all photo of one album) independently. please I need your help.

this is my code (App.js):

import React, { Component } from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
import Photolist from './photolist';
// import logo from './logo.svg';
import './App.css';

class App extends Component {
  constructor(props){
        super(props);
        this.state = {
          currentAlbumId: null,
          CurrentPhotoId: null
        };
        this.updateCurrentAlbum = this.updateCurrentAlbum.bind(this);
    }

  updateCurrentAlbum(id) {
    this.setState({currentAlbumId : id})
  }

  render() {

    const tab_albums = [
      {
        "albumId": 1,
        "id": 1,
        "title": "accusamus beatae ad facilis cum similique qui sunt",
        "url": "http://placehold.it/600/92c952",
        "thumbnailUrl": "http://placehold.it/150/92c952"
      },
      {
        "albumId": 1,
        "id": 2,
        "title": "reprehenderit est deserunt velit ipsam",
        "url": "http://placehold.it/600/771796",
        "thumbnailUrl": "http://placehold.it/150/771796"
      },
      {
        "albumId": 2,
        "id": 66,
        "title": "provident rerum voluptatem illo asperiores qui maiores",
        "url": "http://placehold.it/600/ee0a7e",
        "thumbnailUrl": "http://placehold.it/150/ee0a7e"
      },
      {
        "albumId": 2,
        "id": 67,
        "title": "veritatis labore ipsum unde aut quam dolores",
        "url": "http://placehold.it/600/1279e9",
        "thumbnailUrl": "http://placehold.it/150/1279e9"
      }
    ]; 
    const albumsIds = [];
    
    tab_albums.map((album_model) => {
        return (
            albumsIds.indexOf(album_model.albumId) === -1 ? albumsIds.push(album_model.albumId) : null
        )
    });

    var album_style = {"background": "#cccccc", "marginBottom": "10px", "borderLeft": "5px solid red"};
    var style_div = {"width": "50%", "float": "left"};

    const liste_album = albumsIds.map((alb_id) => {
          return (
            <Router key={alb_id}>
                <li style={album_style} >
                    <Link to={"/photolist/"+alb_id} onClick={() => this.updateCurrentAlbum(alb_id)}>
                    Album : { alb_id }
                    </Link>
                    <Route path="/photolist" component={Photolist}/>
                </li>
            </Router> 
          )
    });
  return (
    <div className="App">
      <div style={style_div}>
         <ul>{liste_album}   </ul>
      </div>
      <div style={style_div}>
        <button>wishlist</button>
      </div>
    </div>
    ); 
  }
}
export default App;

回答1:


It seems like you render your links together with the what you believe should be the Router information, but that is not really how it works

What you should do however is render the links independently, and to view one item of your fotolist, to add a matching route in your rendering app.

So change the rendering of your links, like so:

const liste_album = albumsIds.map((alb_id) => {
      return (
            <li style={album_style} key={alb_id}>
                <Link to={"/photolist/"+alb_id}>
                Album : { alb_id }
                </Link>
            </li>
      )
});

Removing the onClick handler, as this is done by the Link component. When it navigates, your app would know that the browser location has changed. To show the current active route, you should change your render some like this (note that the Router is defined here, and that you should just define a template path to match the active Route, or an exact path, depending on your need)

return (
  <Router>
    <div className="App">
      <div style={style_div}>
        <ul>{liste_album}</ul>
      </div>
      <div style={style_div}>
        <button>wishlist</button>
      </div>
      <Route path="/photolist/:id" render={({match}) => <Photolist alb_id={match.params.id} />
    </div>
  </Router>
  ); 
}

This line

<Route path="/photolist/:id" render={({match}) => <Photolist alb_id={match.params.id} />

will find any path that has a parameter and will render a certain component. At the moment I am passing along the alb_id, however you could choose to search for the matching album, and pass that to the Photolist.

Alternatively, you could also create the path as:

<Route path="/photolist/:id" component={Photolist} />

And export the Photolist component (which would be a single album, I guess) with the withRouter Higher Order Function, which would enable you to get the params at least from the router props. However, that wouldn't completely work in your current setting, as your code seems to mix data with rendering and state mapping. This is well, not perfect. Your component also seems to do way to much.

You should really create 3 or four components from that one component you have just shown, as an example, check this version of your photolist:

// just some imports matching with the cdn packages
const { Route, withRouter } = ReactRouter;
const { Link } = ReactRouterDOM;
const { createStore, combineReducers } = Redux;
const { Provider, connect } = ReactRedux;
const { Component } = React;

const Router = ReactRouter.MemoryRouter;

const tab_albums = [
  {
    "albumId": 1,
    "id": 1,
    "title": "accusamus beatae ad facilis cum similique qui sunt",
    "url": "http://placehold.it/600/92c952",
    "thumbnailUrl": "http://placehold.it/150/92c952"
  },
  {
    "albumId": 1,
    "id": 2,
    "title": "reprehenderit est deserunt velit ipsam",
    "url": "http://placehold.it/600/771796",
    "thumbnailUrl": "http://placehold.it/150/771796"
  },
  {
    "albumId": 2,
    "id": 66,
    "title": "provident rerum voluptatem illo asperiores qui maiores",
    "url": "http://placehold.it/600/ee0a7e",
    "thumbnailUrl": "http://placehold.it/150/ee0a7e"
  },
  {
    "albumId": 2,
    "id": 67,
    "title": "veritatis labore ipsum unde aut quam dolores",
    "url": "http://placehold.it/600/1279e9",
    "thumbnailUrl": "http://placehold.it/150/1279e9"
  }
]; 

const albumsReducer = (state = tab_albums, action) => {
  // currently not doing anything specials, so just load the albums in the state
  return state;
}

// the store will trigger all changes down to any components intrested in it, thanks to the Provider component
const store = createStore( combineReducers({ albums: albumsReducer }) );

// redux helper to match state to the props, so that components can access the defined properties inside its props
const albumsStateToProps = state => ({ albums: state.albums });

// the album is a purely stateless component, it simply receives the props it should show
const Album = ({ albums, currentAlbumId }) => {
  // get the album based on the passed currentAlbumId
  let album = albums.find(a => {
    return a.id == currentAlbumId;
  });
  return <div>
    <h1>Album: {album.title}</h1>
    <div>
      <a href={album.url} target="_blank"><img src={album.thumbnailUrl} alt="album image" /></a>
    </div>
    <Link to="/">Back to mainpage</Link>
  </div>;
};

const ConnectedAlbum = connect( albumsStateToProps )( Album );

// the location is received thanks to the withRouter higher order function
const PathViewer = ({location}) => {
  return <span>{ location.pathname }</span>;
};

// this creates a new component that extends the props of the PathViewer
const RouterPathViewer = withRouter( PathViewer );

// the overview component will receive the albums through its props, and just needs to map it
const Overview = ( {albums} ) => {
  return <ul>{albums && albums.map( ({id, title}) => <li><Link to={`/album/${id}`}>{ title }</Link></li> ) }</ul>;
};

// the ConnectedOverview gets its props thanks to the albumsStateToProps method defined above
// this means that the Overview component will have access to props.albumns
const ConnectedOverview = connect(albumsStateToProps)(Overview);

// this is the layout component, it enables the routing, and shows the main page
class AlbumViewer extends Component {
  render() {
    return <Router>
      <div>
        <h1>Album viewer</h1>
        <span>Current path: <RouterPathViewer /></span>
        <Route path="/" exact={true} component={ConnectedOverview} />
        <Route path="/album/:id" render={({match}) => {
          return <ConnectedAlbum currentAlbumId={match.params.id} />;
        } }/>
      </div>
    </Router>;
  }
}

// this creates the rendering, where the Provider will rerender components depending on it based on changes on the store
ReactDOM.render( <Provider store={store}><AlbumViewer /></Provider>, document.querySelector('#container') );
<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://cdnjs.cloudflare.com/ajax/libs/react-router/4.2.0/react-router.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/4.2.2/react-router-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.min.js"></script>
<div id="container"></div>

I welcome you to finish the small demo I made, or if you might have questions towards the setup, and why so many packages are need to run this small app, so feel free to play around with it ;)



来源:https://stackoverflow.com/questions/47521244/react-js-setstate-with-params-when-onclick

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!