React / Webpack / Django - Uncaught TypeError: Невозможно прочитать свойство 'XXX' из неопределенного - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь создать компонент React под названием «Предложения», который будет отображать табличный список информации, полученной из бэкэнда Django.

Я использую Reactable-Search компонент для формирования таблицы, но я продолжал получать сообщение об ошибке при попытке сопоставить значения this.props.proposals, такие как id и proj_name, с ячейки таблицы - Uncaught TypeError: Невозможно прочитать свойство 'ячейки' с неопределенным значением

На самом деле не уверен, почему, потому что когда я отображаю this.props.proposals непосредственно в рендере, возвращающем типичные теги html-таблицы, он работает, то есть хорошо обрабатывает данные бэкэнда. и я также использовал компонент Reactable-Search с отображением в других случаях, и он работал нормально.

Вывод журнала this.props.proposals также показывает правильный массив объектов ...: screenshot

Действительно признателен, если кто-то может подтолкнуть меня в правильном направлении, спасибо!

Компонент предложений:

import React, { Component } from "react";
import { connect } from "react-redux";
import SearchTable from "reactable-search";
import { proposals } from "../actions";

class Proposals extends Component {
  componentDidMount() {
    this.props.fetchProposals();
  }
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    var rows = this.props.proposals.map(p => ({
      selected: this.state.selectedRow === p.id,
      onClick: () => {
        this.setState({ selectedRow: p.id });
      },
      cells: {
        "#": p.id,
        "Project Name": p.proj_name
      }
    }));

    return (
      <SearchTable
        showExportCSVBtn={true}
        searchPrompt="Type to search"
        rows={rows}
      />
    );
  }
}

const mapStateToProps = state => {
  return {
    proposals: state.proposals
  };
};

const mapDispatchToProps = dispatch => {
  return {
    fetchProposals: () => {
      dispatch(proposals.fetchProposals());
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Proposals);

Предложения редуктора:

const initialState = [];

export default function proposals(state = initialState, action) {
  switch (action.type) {
    case "FETCH_PROPOSALS":
      return [...action.proposals];

    default:
      return state;
  }
}

Предложения акции

export const fetchProposals = () => {
  return dispatch => {
    let headers = { "Content-Type": "application/json" };
    return fetch("/api/proposals/", { headers })
      .then(res => res.json())
      .then(proposals => {
        return dispatch({
          type: "FETCH_PROPOSALS",
          proposals
        });
      });
  };
};

1 Ответ

0 голосов
/ 23 января 2019

Проблема в том, что вы запрашиваете предложения асинхронно, но компонент SearchTable, похоже, не работает с пустым объектом начальных предложений. Попробуйте передать пустой массив в качестве rows prop, и вы получите точно такое же сообщение об ошибке для неопределенного объекта.

Чтобы это исправить, вам нужно показать индикатор загрузки вместо SearchTable во время получения предложений. Ваш редуктор должен выглядеть примерно так, за исключением того, что вы должны также обрабатывать случай отказа:

const initialState = { isLoading: false, error: null, proposals: [] };

export default function proposals(state = initialState, action) {
  switch (action.type) {
    case "FETCH_PROPOSALS":
      return {
        ...state,
        isLoading: true
      };
    case "FETCH_PROPOSALS_SUCCESS":
      return {
        ...state,
        isLoading: false,
        proposals: action.proposals
      };
    case "FETCH_PROPOSALS_FAILURE":
      return {
        ...state,
        isLoading: false,
        error: action.error,
      };
    default:
      return state;
  }
}

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

import React, { Component } from "react";
import { connect } from "react-redux";
import SearchTable from "reactable-search";
import { proposals } from "../actions";

class Proposals extends Component {
  componentDidMount() {
    this.props.fetchProposals();
  }
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    const { proposals, error, isLoading } = this.props;

    if (isLoading) {
      return <div>Loading...</div>;
    }
    if (error) {
      return <div>{error.message}</div>;
    }
    if (proposals.length === 0) {
      return <div>No proposals</div>;
    }

    var rows = proposals.map(p => ({
      selected: this.state.selectedRow === p.id,
      onClick: () => {
        this.setState({ selectedRow: p.id });
      },
      cells: {
        "#": p.id,
        "Project Name": p.proj_name
      }
    }));

    return (
      <SearchTable
        showExportCSVBtn={true}
        searchPrompt="Type to search"
        rows={rows}
      />
    );
  }
}

const mapStateToProps = state => {
  return {
    proposals: state.proposals.proposals,
    isLoading: state.proposals.isLoading,
    error: state.proposals.error,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    fetchProposals: () => {
      dispatch(proposals.fetchProposals());
    }
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Proposals);

И громовой экшн:

export const fetchProposals = () => {
  return dispatch => {
    dispatch({type: "FETCH_PROPOSALS"});
    let headers = { "Content-Type": "application/json" };
    return fetch("/api/proposals/", { headers })
      .then(res => res.json())
      .then(proposals => {
        dispatch({
          type: "FETCH_PROPOSALS_SUCCESS",
          proposals
        });
      })
      .catch(error => {
        dispatch({
          type: "FETCH_PROPOSALS_FAILURE",
          error,
        });
      });
  };
};
...