Сопоставление объекта JavaScript, полученного с сервера Express, в React

У меня есть несколько наборов внутри объекта для разных типов питательных микроэлементов (витаминов, минералов, антиоксидантов, аминокислот и омега). Я хочу, чтобы каждый из них передавался как <option> внутри своего собственного раскрывающегося меню, в котором отображается их собственный тип (например, витамины: витамин B2, B6, B5 и т. Д.)

Моя консоль показывает, что данные были успешно получены:

{vitamins: Array(6), minerals: Array(2), aminoacids: Array(1), antioxidants: Array(2), omegas: Array(1)}

Но когда я дохожу до Vitamins.js, где пытаюсь отобразить параметры, он говорит: «TypeError: vitamins.map не является функцией».

Вот мой файл res.json с моего сервера Express:

 router.get('/', function(req, res, next) {
 res.json({vitamins: [
  {
    name: "Vitamin B2"
  },
  {
    name: "Vitamin B6"
  }, 
  {
    name: "Vitamin B5"
  }
],
 minerals: [
  {
    name: "Calcium"
  },
 {
  name: "Zinc"
  }
 ] 
});
});

Здесь объявляется состояние для res.json в Home.js. (Я знаю, что слово «пользователи» не имеет смысла прямо сейчас, и я изменю его. Просто пытаюсь сначала выяснить, почему это не работает.)

constructor(props) {
  super(props);
  this.state = {users: []};
 }

 componentDidMount() {
   fetch('/users')
      .then(res => res.json())
      .then(users => this.setState({ users }));
 }

 render() {
   return (
      <MenuItems users={this.state.users} />
  )
}

Это Menuitems.js, на один уровень ниже Home.js, где я вызываю функцию handleChange, поэтому, когда пользователь нажимает на новый микро (Vitamin B2 -> B6), новый микро (B6) становится новым выбранным параметром.

 constructor(props) {
  super(props);
   this.state = {
    value: '',
  };

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

handleChange(e) {
  const { value } = e.target;
   this.setState({
      value: value
   });
 }

 render() {
  return (
    <ul className="menuitems">
      <li>
        <Vitamins data={this.props.users} />
      </li>
      <li>
         <Minerals data={this.props.users} />
       </li>
    </ul>
  )
}

И это Vitamins.js (у меня также есть Minerals.js, AminoAcids.js и т. Д.), Где я пытаюсь сопоставить массив «витаминов» в моем объекте res.json и вытащить каждое имя внутри выделенного падать.

  renderData() {
    const vitamins = this.props.data;
    console.log("the data", vitamins);
    return vitamins.map((micro, index) => {
     return (
       <option value={micro.value} key={index}>{micro.name}</option>
     )
   })
 }

  render() {
   return (
    <form>
      <select value={this.props.value} onChange={this.handleChange}>
        <option value="" selected>--Vitamins--</option>
        {this.renderData()}
      </select>
    </form>
  )
 }

Console.log в renderData () сначала ничего не покажет, но после componentDidMount () он показывает это ДВАЖДЫ

 the data {vitamins: Array(6), minerals: Array(2), aminoacids: Array(1), antioxidants: Array(2), omegas: Array(1)}

ИЗМЕНИТЬ

Я последовал некоторым советам, которые мне дали, и попробовал применить их.

Этот сработал, где я объявил состояние внутри каждого дочернего компонента (витамины, минералы и т. Д.)

 state = {users: []};

 componentDidMount() {
   fetch('/users')
    .then(res => res.json())
    .then(users => {
      this.setState({
        users: users.vitamins
      });
    })
  }

 renderData() {
   const vitamins = this.state.users;
   return vitamins.map((micro, index) => {
   return (
      <option value={micro.value} key={index}>{micro.name}</option>
    )
  })
}

Однако я не хочу постоянно получать данные в каждом компоненте. Я попытался объявить состояние в родительском MenuItems и передать {this.state.users.vitamins} в свойствах данных, как было предложено.

(MenuItems.js)

state = {users: []};

componentDidMount() {
  fetch('/users')
    .then(res => res.json())
    .then(users => {
     this.setState({
       users
     });
   })
 }

render() {
  return (
    <ul className="menuitems">
      <li>
        <Vitamins data={this.state.users.vitamins} />
      </li>
    </ul>
 )
}

(Vitamins.js)

 renderData() {
   const vitamins = this.props.data;
   return vitamins.map((micro, index) => {
     return (
       <option value={micro.value} key={index}>{micro.name}</option>
     )
   })
 }

Но при этом я получаю «TypeError: Cannot read property 'map' of undefined», когда я делаю это.

Если я сделаю «const {vitamins} = this.props.data», я получу «TypeError: Cannot read property 'vitamins' of undefined».

Итак, как мне выполнить выборку на родительском уровне и передать единственный нужный мне массив в data =?


person John White    schedule 25.05.2018    source источник
comment
попробуйте const { vitamins } = this.props.data или: <Vitamins data={this.props.users.vitamins} /> + this.state = { users: {} };   -  person Roy Wang    schedule 25.05.2018
comment
Кажется, что @riwu верен. Данные, переданные в Vitamins.js, являются родительской ветвью массива витаминов.   -  person Jan Ciołek    schedule 25.05.2018
comment
Это большой объем кода, большая часть которого не должна иметь никакого отношения к делу: что входит в качестве реквизита и состояния для вашего компонента «Витамины»? Потому что это единственное, что имеет значение: если он получает простой объект, тогда на 100% очевидно, почему .map() терпит неудачу. Сделайте console.log(this.props) в этой функции рендеринга витаминов, посмотрите, что вы на самом деле добавили, а затем вернитесь обратно.   -  person Mike 'Pomax' Kamermans    schedule 25.05.2018
comment
@ Mike'Pomax'Kamermans Если я разделю пользователей здесь <Vitamins data={this.state.users.vitamins} />, я вернусь undefined в console.log из функции рендеринга в Vitamins.js. Если я сделаю <Vitamins data={this.state.users} />, я верну весь объект пользователей в console.log {vitamins: Array(6), minerals: Array(2), aminoacids: Array(1), antioxidants: Array(2), omegas: Array(1)}.   -  person John White    schedule 25.05.2018
comment
Так что сократите это резко. Сделайте console.log this.state.users перед сборкой Vitamins: скопируйте эту строку, выбросьте ВСЕ, что у вас есть, создайте class App extends React.Component { render() { let userstate = JSON.parse("...that string..."); return <Vitamins data={userstate}/>; } } и посмотрите, что произойдет. Вот почему обычно советуют сформировать минимально воспроизводимый пример: почти ни один из показанного вами кода не требуется для на самом деле проверьте, что делает ваш витаминный класс. И если все в порядке, мы повторяем упражнение на один компонент выше (захватываем данные как JSON, загружаем их напрямую).   -  person Mike 'Pomax' Kamermans    schedule 26.05.2018


Ответы (1)


В настоящее время вы загружаете только пользователей. В vitamins.js вы пытаетесь получить данные из this.props.data, который предоставит вам информацию о том, что вы имеете дело с массивами, как показано в журнале консоли. Вам необходимо «разделить» ваши данные, прежде чем вы сможете их получить.

this.setState({ users: users.vitamins)} 

например (я не знаю вашу экосистему, поэтому все, что вы пытаетесь получить, необходимо связать с событием setState, иначе вы просто передаете данные без определения данных).

Как уже было опубликовано в разделе комментариев, вы также можете попытаться разделить его при передаче данных в компонент:

    <Vitamins data={this.props.users.vitamins}

Это в основном то же самое, вы берете пользователей и получаете данные о витаминах. Всякий раз, когда вы хотите отобразить массив, вы должны «открыть» его.

Вот базовый пример того, как вы обычно это делаете:

  .then(response => response.json())
  .then(data => data.users.your-data-you-want-to-fetch.map((x) => {

    this.setState({
      vitamin: x.name,
      id: x.id,
      })

}))

После этого data.vitamin / data.id будет доступен для ваших данных props.

Привет!

person Prometheus    schedule 25.05.2018