3

I'm fairly new to React. I'm trying to build a site where you can click navigation item (in this case music genre) and it will list all the songs that belongs to that particular genre.

My app.js looks like this:

import React, { Component } from 'react';
import ListGenres  from './ListGenres';
import './App.css';


class App extends Component {
    constructor(props) {
      super();
      this.state = {dataList: props.dataList};
    }
    render() {
      return (
        <div>
          <div className="App">
            <Navigation tracks = {this.state.dataList} />
            <ListGenres tracks = {this.state.dataList}/>
          </div>
        </div>
     );
   }
}

export default App;

I have a navigation component that looks like this:

import React from 'react';
import HeaderBar  from './HeaderBar'; 
import MenuItem from 'material-ui/MenuItem';

export class Navigation extends React.Component {
constructor(props) {
    super();
}
/*
onClickFunction() {
    toggle elements in another component
}
*/
render() {
    const genres = this.props.tracks.map((elem) => {
        return elem.genre;
    });
    const filtered = genres.filter((elem, index, self) => {
      return self.indexOf(elem) === index;
    });
    const genreLoop = filtered.map((elem, i) => {
      return (
        <MenuItem 
            onClick= {this.onClickFunction}
            key={ i }><a>{ elem }</a>
        </MenuItem>);
    });
    return (
        <div>
            { genreLoop }
        </div>
    );
  }
}
export default Navigation;

My list of items are rendered in another component whick looks like this:

import React from 'react';
import './ListGenres.css';

export class ListGenres extends React.Component {
    constructor(props) {
    super();
}
render() {
    return (
        <div className="genreList">
            <div className="tracklist-visible tracklist-pop">
                <ul>
                    <h3>Pop</h3>
                    { this.tracklist('Pop') }   
                </ul>
            </div>
            <div className="tracklist-visible tracklist-metal">
                <ul>
                    <h3>Pop</h3>
                    { this.tracklist('Metal') } 
                </ul>
            </div>
        </div>
   );
}

Is there way to maybe add css-class to tracklist-div when anchor is clicked from Navigation-component? Looks like I can't pass any props from that component since it's "stand-alone"-component?

6
  • What is the relation between the two components? Where is the onClickFunction function? Commented Sep 15, 2017 at 12:38
  • Sorry, there was none. That was just my fiddling that left there. Now added the function between comment. Commented Sep 15, 2017 at 12:43
  • If the code is not relevant - remove it. If it's relevant - make sure there is a connection.. Commented Sep 15, 2017 at 12:44
  • The components are not connected, they only get mutual props from upper-level component. Commented Sep 15, 2017 at 12:45
  • So add the upper level component so we can understand the context. Commented Sep 15, 2017 at 12:46

2 Answers 2

3

You need to lift the state up.

Of course, you can solve this with Redux too, but let's keep it simple and only use React.

Lifting State Up

Create a component that will contains both <Navigation /> and <ListGenres /> components.

Keep the state (genre and selectedGenre) in this parent component and pass it down through props.

You also need to create a callback to handle genres changes.

Here's the example:

class App extends Component {
  constructor (props) {
    super(props)
    this.state = {
      selectedGenre: null,
      genres: [...]
    }
  }

  onGenreChange (genre) {
    this.setState({ selectedGenre: genre })
  }

  render () {
    return (
      <div>
        <Navigation
          onGenreChange={genre => this.onGenreChange(genre)}
          genres={this.state.genres}
        />
        <ListGenres
          genres={this.state.genres}
          selectedGenre={this.state.genres}
        />
      </div>
    )
  }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Yes, this did the trick! This is what i was basically trying to do, lifting state up! Thank you, sir!
1

You didn't supply much code or example on how things should work but as i understand you are looking for a behavior similar to Tabs, where you click a Tab and a corresponding View is presented.
If this is the case, then you need a Parent component that will manage the selected Tabs and render the View respectively. This is a simple example:

const tabs = ["Pop", "Rock", "Rap", "Electro"];

const View = ({name}) => <h1 className="view">{`This is ${name} music!`}</h1>

class Tab extends React.Component {
  constructor(props) {
    super(props);

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

  onClick() {
    const { id, onClick } = this.props;
    onClick(id);
  }

  render() {
    const { name, id, isSelected } = this.props;
    const css = `tab ${isSelected && 'selected'}`;
    return (
      <div
        className={css}
        onClick={this.onClick}
      >
      {name}
      </div>
    );
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      selectedTab: 1
    }

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

  onTabChange(id) {
    this.setState({ selectedTab: id });
  }

  render() {
    const { selectedTab } = this.state;
    return (
      <div>
        {
          tabs.map((t, i) => {
              return (
              <div className="wrapper">
                <Tab name={t} id={i + 1} isSelected={selectedTab === i + 1} onClick={this.onTabChange} />
                {selectedTab == i + 1 && <View name={t} />}
              </div>
              )
            })
        }
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById('root'));
.tab {
  display: inline-block;
  margin: 0 10px;
  width: 60px;
  text-align: center;
  cursor: pointer;
  padding: 5px;
}

.selected {
  border: 1px solid #eee;
  box-shadow: 0 0 3px 1px #999;
}

.wrapper{
  display:inline-block;
}

.view{
  position: absolute;
  top: 0;
  left: 0;
  margin-top: 50px;
  text-align: center;
}
<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>
<div id="root"></div>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.