Search code examples
reactjslistmaterial-uilistitem

Pass state value to component


I am really new in React.js. I wanna pass a state (that i set from api data before) to a component so value of selectable list can dynamically fill from my api data. Here is my code for fetching data :

getListSiswa(){
  fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
  .then(posts => {
    return posts.json();
  }).then(data => {
    let item = data.posts.map((itm) => {
      return(
        <div key={itm.siswa_id}>
          <ListItem
            value={itm.siswa_id}
            primaryText={itm.nama}
          />
        </div>
      )
    });

  this.setState({item: item});
  });
}

From that code, i set a state called item. And i want to pass this state to a component. Here is my code :

const ListSiswa = () => (
  <SelectableList>
    <Subheader>Daftar Siswa</Subheader>
      {this.state.item}
  </SelectableList>
);

But i get an error that say

TypeError: Cannot read property 'item' of undefined

I am sorry for my bad explanation. But if you get my point, i am really looking forward for your solution.

Here is my full code for additional info :

import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {List, ListItem, makeSelectable} from 'material-ui/List';
import Subheader from 'material-ui/Subheader';

let SelectableList = makeSelectable(List);

function wrapState(ComposedComponent) {
  return class SelectableList extends Component {
    static propTypes = {
      children: PropTypes.node.isRequired,
    };

    getListSiswa(){
      fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
      .then(posts => {
        return posts.json();
      }).then(data => {
        let item = data.posts.map((itm) => {
          return(
            <div key={itm.siswa_id}>
              <ListItem
                value={itm.siswa_id}
                primaryText={itm.nama}
              />
            </div>
          )
        });

      this.setState({item: item});
      });
    }

    componentWillMount() {
      this.setState({
        selectedIndex: this.props.defaultValue,
      });

      this.getListSiswa();
    }

    handleRequestChange = (event, index) => {
      this.setState({
        selectedIndex: index,
      });
    };

    render() {
      console.log(this.state.item);
      return (
        <ComposedComponent
          value={this.state.selectedIndex}
          onChange={this.handleRequestChange}
        >
          {this.props.children}
        </ComposedComponent>
      );
    }
  };
}

SelectableList = wrapState(SelectableList);

const ListSiswa = () => (
  <SelectableList>
    <Subheader>Daftar Siswa</Subheader>
    {this.state.item}
  </SelectableList>
);

export default ListSiswa;

Solution

  • One way to do it is by having the state defined in the parent component instead and pass it down to the child via props:

    let SelectableList = makeSelectable(List);
    
    function wrapState(ComposedComponent) {
      return class SelectableList extends Component {
        static propTypes = {
          children: PropTypes.node.isRequired,
        };
    
        componentWillMount() {
          this.setState({
            selectedIndex: this.props.defaultValue,
          });
    
          this.props.fetchItem();
        }
    
        handleRequestChange = (event, index) => {
          this.setState({
            selectedIndex: index,
          });
        };
    
        render() {
          console.log(this.state.item);
          return (
            <ComposedComponent
              value={this.state.selectedIndex}
              onChange={this.handleRequestChange}
            >
              {this.props.children}
              {this.props.item}
            </ComposedComponent>
          );
        }
      };
    }
    
    SelectableList = wrapState(SelectableList);
    
    class ListSiswa extends Component {
      state = {
        item: {}
      }
    
      getListSiswa(){
        fetch('http://localhost/assessment-app/adminpg/api/v1/Siswa/')
        .then(posts => {
          return posts.json();
        }).then(data => {
          let item = data.posts.map((itm) => {
            return(
              <div key={itm.siswa_id}>
                <ListItem
                  value={itm.siswa_id}
                  primaryText={itm.nama}
                />
              </div>
            )
          });
    
        this.setState({item: item});
        });
      }
    
      render() {
        return (
          <SelectableList item={this.state.item} fetchItem={this.getListSiswa}>
            <Subheader>Daftar Siswa</Subheader>
          </SelectableList>
        );
      }
    }
    
    export default ListSiswa;
    

    Notice that in wrapState now I'm accessing the state using this.props.item and this.props.fetchItem. This practice is also known as prop drilling in React and it will be an issue once your app scales and multiple nested components. For scaling up you might want to consider using Redux or the Context API. Hope that helps!