Реагируйте на прослушивание обновленных данных в службе, а затем передайте данные администратору dataProvider в другом компоненте. - PullRequest
1 голос
/ 24 января 2020

Еще новичок в React. Как утверждает субъект, я хочу прослушать данные, поступающие через мой сервис, а именно:

import axios from "axios"

const base_url = 'https://localhost:5001/OrderItem';

export let orderItemByDivision = {
    data: []
}

export const getOrderItemByDiv = (div, yr) => {
    orderItemByDivision.data = axios.get(base_url + "/" + div + "/" + yr);
    return axios.get(base_url + "/" + div + "/" + yr)
}

Вышеупомянутый сервис запускается выбором года в раскрывающемся меню с последующим выбором деление, которое отправляет get в API, отвечая JSON

import React from 'react';
import { FormGroup, FormControl, Button, IconButton, Menu, MenuItem } from '@material-ui/core';
import { getDivision } from '../services/division-service';
import { getOrderItemByDiv } from '../services/order-item-service';
import { MuiThemeProvider } from '@material-ui/core/styles';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import theme from '../theme';

export  default class DivisionDropDownComponent extends React.Component {
    state = {
        divData: [],
        divValue: 'Select Division',
        divOpen: false,
        divAnchorEl: null,
        yrValue: '2020',
        yrOpen: false,
        yrAnchorEl: null,
        yrs: ['2020','2019','2018','2017','2016','2015','2014','2013','2012']
    }

    constructor(props) {
        super(props);

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

    componentDidMount() {
        getDivision()
            .then((res) => {
                this.setState({ divData: res.data });
            })
            .catch(error => {
                console.error(error);
            });
    }

    handleDivChange = (divData) => {
        // this.setState({value: event.target.value ? event.target.value : ''});
        console.log("divData selected: " + divData.code_division); 
        getOrderItemByDiv(divData.code_division, this.state.yrValue)
            .then((res) => {
                console.log(res.data);
            })
            .catch(error => {
                console.error(error);
            });
        this.onCloseDiv();
    }

    handleYrChange = (event) => {
        // this.setState({value: event.target.value ? event.target.value : ''});
        this.setState({ yrValue: event.target.value });
        console.log("divData selected: " + event.target.value);
        this.onCloseYr();
    }

    handleDivClick = (event) => {
        this.setState({ divAnchorEl: event.target })
        this.setState({ divOpen: true });
    }

    handleYrClick = (event) => {
        this.setState({ yrAnchorEl: event.target })
        this.setState({ yrOpen: true });
    }

    onCloseDiv = () => {
        this.setState({ divOpen: false });
    }

    onCloseYr = () => {
        this.setState({ yrOpen: false });
    }

    render(){
        let arrayOfData = this.state.divData;
        let dropdowns = arrayOfData.map((divData) =>               
            <MenuItem onClick={(event) => this.handleDivChange(divData)} key={divData.code_division}
            value={divData.code_division} text={divData.code_division}>
                {divData.code_division}
            </MenuItem>
        );
        let arrayOfYrs = this.state.yrs;
        let yrDropDown = arrayOfYrs.map((yrs) =>
            <MenuItem onClick={(event) => this.handleYrChange(event)} value={yrs} key={yrs}>
                {yrs}
            </MenuItem>           
        );
        return (
            <MuiThemeProvider theme={theme}>
                <FormGroup column='true'>
                    <FormControl>
                    <IconButton
                        aria-label="more"
                        aria-controls="long-menu"
                        aria-haspopup="true"
                        onClick={this.handleDivClick}
                    >
                        <MoreVertIcon />
                    </IconButton>
                        <Menu id="div-menu" anchorEl={this.state.divAnchorEl} open={this.state.divOpen} onClose={this.onCloseDiv}
                        className='dropDownDiv' defaultValue={this.state.divValue ? this.state.divValue: ''} >
                            <MenuItem value="Select Division">
                                Select Division
                            </MenuItem>
                            {dropdowns}
                        </Menu>
                        <Button aria-controls="simple-menu" aria-haspopup="true" onClick={this.handleYrClick}>
                            Select Year
                        </Button>
                        <Menu id="yrs-menu" open={this.state.yrOpen}
                        anchorEl={this.state.yrAnchorEl}  onClose={this.onCloseYr}
                        defaultValue={this.state.yrValue ? this.state.yrValue: ''} >
                            {yrDropDown}
                        </Menu>
                    </FormControl>
                </FormGroup>
            </MuiThemeProvider>
        )
    }

}

data in console

Как видно на снимке экрана, данные проходя через консоль. Как заставить администратора dataProvider прослушивать любые изменения, поступающие из файла order-item-service.jsx?

Можно ли поместить наблюдаемое в orderItemByDivision.data?

Как обычно, спасибо заранее

Редактирую мой пост, чтобы более подробно ответить на мой вопрос. В Angular я использовал HttpClient и Rx JS для подписки и наблюдения. Я попробовал следующее в React, но у него есть проблемы с setState:

import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";

const base_url = "https://localhost:5001/OrderItem";

export let orderItemByDivision = {
data: []
};

export const getOrderItemByDiv = (div, yr) => {
return new Observable(observe => {
    orderItemByDivision.data = ajax
    .get(base_url + "/" + div + "/" + yr)
    .subscribe(resu => {
        setState({ orderItemByDivision.data:  resu });
        observe.next(resu);
    });
});
};

См. "OrderItemByDivision.data", у которого есть проблемы с точечной нотацией. Поскольку это служба, а не класс компонента, я не могу вызвать this.setState. Как я могу установить состояние, чтобы я мог наблюдать и подписаться на запрос ajax?

Заранее спасибо

Ответы [ 2 ]

0 голосов
/ 01 февраля 2020

ОК, я смог подражать тому, что я сделал в Angular 6, то есть создать заметку, на которую можно подписаться (прислушиваться к изменениям)

Во-первых, сервис order-item-service .jsx:

import { Subject } from 'rxjs';
import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";

const subject = new Subject();
const base_url = 'https://localhost:5001/OrderItem';

export let orderItemByDivision = {
    data: []
}

export const getOrderItemByDiv = (div, yr) => {
    return new Observable(observe => {
        orderItemByDivision.data = ajax
        .get(base_url + "/" + div + "/" + yr)
        .subscribe(resu => {
            orderItemByDivision.data = resu.response ;
            observe.next(resu.response);
        });
    });
};

export const messageService = {
    sendMessage: message => subject.next({ message }),
    clearMessage: () => subject.next(),
    getMessage: () => subject.asObservable()
}

Как видите, я пытался использовать Subject из rx js для отправки данных между компонентами, но использование ajax из rxjs / ajax было тем, что в конечном итоге привело меня чтобы иметь возможность прослушивать изменения

Я продолжил то же самое с сервисом Division-Service.jsx:

import { ajax } from "rxjs/ajax";
import { Observable } from "rxjs";

const base_url = 'https://localhost:5001/Division';

let state = {
    data: []
}

export const getDivision = () => {
    return new Observable(observe => {
        state.data = ajax
        .get(base_url)
        .subscribe(resu => {
            state.data = resu.response ;
            observe.next(resu.response);
        });
    });
}

Как вы можете видеть в обоих случаях, он создает на наблюдаемом после него подписывается на метод get - другими словами, каждый раз, когда используется URL-адрес API, он будет предупреждать любого, кто подписан на два вышеупомянутых метода.

Следующий компонент Division-dropdown-component.jsx получает: деление JSON, заполняющее выпадающее меню делений. Как только конечный пользователь нажимает на подразделение, подписывается getOrderItemByDiv, который возвращает JSON, который нам нужен для заполнения таблицы @ material-ui, перечисленной ниже:

    import React from "react";
    import {
    FormGroup,
    FormControl,
    Button,
    Menu,
    MenuItem
    } from "@material-ui/core";
    import { getDivision } from "../services/division-service";
    import { getOrderItemByDiv } from "../services/order-item-service";
    import { MuiThemeProvider } from "@material-ui/core/styles";
    import theme from "../theme";
    import { messageService } from "../services/order-item-service";

    export default class DivisionDropDownComponent extends React.Component {
    state = {
        divData: [],
        divValue: "Select Division",
        divOpen: false,
        divAnchorEl: null,
        yrValue: "2020",
        yrOpen: false,
        yrAnchorEl: null,
        yrs: [
        "2020",
        "2019",
        "2018",
        "2017",
        "2016",
        "2015",
        "2014",
        "2013",
        "2012"
        ]
    };

    constructor(props) {
        super(props);

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

    componentDidMount() {
        getDivision().subscribe(res => {
        this.setState({ divData: res });
        });
    }

    handleDivChange = divData => {
        getOrderItemByDiv(divData.code_division, this.state.yrValue).subscribe(
        res => {
            this.setState({ divData: res });
            messageService.sendMessage(res);
        }
        );
        this.onCloseDiv();
    };

    handleYrChange = event => {
        this.setState({ yrValue: event.target.value });
        this.onCloseYr();
    };

    handleDivClick = event => {
        this.setState({ divAnchorEl: event.target });
        this.setState({ divOpen: true });
    };

    handleYrClick = event => {
        this.setState({ yrAnchorEl: event.target });
        this.setState({ yrOpen: true });
    };

    onCloseDiv = () => {
        this.setState({ divOpen: false });
    };

    onCloseYr = () => {
        this.setState({ yrOpen: false });
    };

    render() {
        let arrayOfData = this.state.divData;
        let dropdowns = arrayOfData.map((divData, index) => (
        <MenuItem
            onClick={event => this.handleDivChange(divData)}
            key={index}
            value={divData.code_division}
            text={divData.code_division}
        >
            {divData.code_division}
        </MenuItem>
        ));
        let arrayOfYrs = this.state.yrs;
        let yrDropDown = arrayOfYrs.map(yrs => (
        <MenuItem
            onClick={event => this.handleYrChange(event)}
            value={yrs}
            key={yrs}
        >
            {yrs}
        </MenuItem>
        ));
        return (
        <MuiThemeProvider theme={theme}>
            <FormGroup column="true">
            <FormControl>
                <Button
                aria-controls="simple-menu"
                aria-haspopup="true"
                onClick={this.handleYrClick}
                >
                Select Year
                </Button>
                <Menu
                id="yrs-menu"
                open={this.state.yrOpen}
                anchorEl={this.state.yrAnchorEl}
                onClose={this.onCloseYr}
                defaultValue={this.state.yrValue ? this.state.yrValue : ""}
                >
                {yrDropDown}
                </Menu>

                <Button
                aria-controls="simple-menu"
                aria-haspopup="true"
                onClick={this.handleDivClick}
                >
                Select Division
                </Button>
                <Menu
                id="div-menu"
                anchorEl={this.state.divAnchorEl}
                open={this.state.divOpen}
                onClose={this.onCloseDiv}
                className="dropDownDiv"
                defaultValue={this.state.divValue ? this.state.divValue : ""}
                >
                <MenuItem value="Select Division">Select Division</MenuItem>
                {dropdowns}
                </Menu>
            </FormControl>
            </FormGroup>
        </MuiThemeProvider>
        );
    }
    }

Ниже приводится Компонент order-item-component.jsx, который заполняет material-ui Таблица:

import React from "react";

import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import { TablePagination } from "@material-ui/core";
import TableFooter from "@material-ui/core/TableFooter";
import { messageService } from "../services/order-item-service";

export default class OrderItemComponent extends React.Component {
  state = {
    data: [],
    _columns: [],
    Header: [],
    totalCount: 10,
    pageSize: 16,
    page: 0
  };

  componentDidMount() {

  componentDidMount() {
      this.subscription = messageService.getMessage().subscribe(message => {
          if (message) {

              this.setState({ data: message.message });
              this.setState({ totalCount: Math.ceil(this.state.data.length / this.state.pageSize) });
              this.setState({ Header: ['order_id', 'order_item_id', 'product_id', 'code_division', 'code_product', 
              'quantity_due', 'quantity_shipped', 'price', 'date_shipped', 'date_due', 
              'customer_id','ship_via','value_due','value_shipped','date_order','date_modified'] });

          } else {
              // do nothing
              this.setState({ data: [] });
          }
      })
  }

  componentWillUnmount() {
      // unsubscribe to ensure no memory leaks
      this.subscription.unsubscribe();
  }

  getOrderItem(){
      this.setState({ data: messageService.getMessage() });
  }

  handleChangePage = (event, newPage) => {
    this.setState({ page: newPage });

    if (this.state.totalCount !== this.state.data.length) {

    }
  }

  render() {

      return (
          <div>
  <TableContainer component={Paper}>
    <Table aria-label="simple table">
      <TableHead>
        <TableRow>
          <TableCell>Order ID</TableCell>
          <TableCell align="right">Qty Due</TableCell>
          <TableCell align="right">Prod ID</TableCell>
          <TableCell align="right">Div</TableCell>
          <TableCell align="right">Prod Code</TableCell>
          <TableCell align="right">Qty Sh</TableCell>
          <TableCell align="right">Price</TableCell>
          <TableCell align="right">Dt SH</TableCell>
          <TableCell align="right">Dt Due</TableCell>
          <TableCell align="right">Cust ID</TableCell>
          <TableCell align="right">Ship Via</TableCell>
          <TableCell align="right">Val Dt</TableCell>
          <TableCell align="right">Val Sh</TableCell>
          <TableCell align="right">Dt Or</TableCell>
          <TableCell align="right">Dt Mod</TableCell>
        </TableRow>
      </TableHead>
      <TableBody>
        {this.state.data.map((row, index) => (
          <TableRow key={ index }>
            <TableCell component="td" scope="row">
              { row.order_id }
            </TableCell>
            <TableCell align="right">{ row.quantity_due }</TableCell>
            <TableCell align="right">{ row.product_id}</TableCell>
            <TableCell align="right">{ row.code_division }</TableCell>
            <TableCell align="right">{ row.code_product }</TableCell>
            <TableCell align="right">{ row.quantity_shipped }</TableCell>
            <TableCell align="right">{ row.price }</TableCell>
            <TableCell align="right">{ row.date_shipped}</TableCell>
            <TableCell align="right">{ row.date_due }</TableCell>
            <TableCell align="right">{ row.customer_id }</TableCell>
            <TableCell align="right">{ row.ship_via }</TableCell>
            <TableCell align="right">{ row.value_due }</TableCell>
            <TableCell align="right">{ row.value_shipped }</TableCell>
            <TableCell align="right">{ row.date_order }</TableCell>
            <TableCell align="right">{ row.date_modified }</TableCell>
          </TableRow>
        ))}
      </TableBody>
      <TableFooter>
        <TableRow>
          <TablePagination
            rowsPerPageOptions={ [5, 10, 25, { label: 'All', value: -1 }] }
            colSpan={ 3 }
            count={ this.state.data.length }
            rowsPerPage={ this.state.pageSize }
            page={ this.state.page }
            SelectProps={ {
              inputProps: { 'aria-label': 'rows per page' },
              native: true,
            } }
            onChangePage={ this.handleChangePage }
          />
        </TableRow>
      </TableFooter>
    </Table>
    </TableContainer>
          </div>
      )
  }

}

Теперь вы можете увидеть, как я использую метод getMessage для установки данных

Почти все это работает, кроме нумерации страниц - когда я нажимаю на стрелку «больше», он будет правильно рассчитывать, какой будет следующий набор данных, но не будет отображать «новые» данные в таблице. Мне нужно будет выяснить, как сделать refre sh для таблицы @ material-ui, когда используется разбиение на страницы, но, тем не менее, я подумал, что поделюсь, если кто-то еще захочет узнать, как создать наблюдаемую область.

Я пытался использовать пример в документации @ material-ui, но setPage = React.useState (5) продолжал вызывать ошибку, связанную с недействительным хуком или чем-то в этом роде - относительно следующей функции:

  const handleChangePage = (event, newPage) => {
     setPage(newPage);
  };

Я опубликую еще один вопрос по этой теме c, но на данный момент на вопрос, который я задал, дан ответ

Еще раз спасибо

0 голосов
/ 24 января 2020

Я просто имел в виду smt вот так.

class ParentComp extends Component {

  constructor (props) {
    super(props);

    this.state = {
      data: {}
    };
  }

  editData = newData => {
    this.setState({
      data: newData
    });
  };

  render () {
    return <ChildComp editData={ this.editData } />;
  }
}

class ChildComp extends Component {

  render () {
    return <Button onCLick={ () => this.props.editData(someData) } />;
  }
}

Вы можете обновить родительское состояние comp из дочернего comp.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...