Не удается заставить функцию onChange работать с React с Grid-контейнерами

Получил изображенную ошибку при попытке настроить обработчик события onChange для функции FormRow. В конечном итоге необходимо сохранить пользовательские данные из времени сброса очереди, порога уровня обслуживания и порога короткого отказа. Queuecontainer — это основной класс, который использует редактор для создания форм-контейнеров.

QuesContainer.js

import React from 'react';
import Editor from './Editor';
import LoginForm from '../LoginForm';
import axios from 'axios';

export default class QueuesContainer extends React.Component {
       constructor(props) {
    super(props);
    this.state = {
        workspaceSID: '',
        apiKey: '',
        apiSecret: '',
        queues: [],
        name: '',
    }
    this.listQueues = this.listQueues.bind(this);
    this.setCredentials = this.setCredentials.bind(this);
    this.getCredentials = this.getCredentials.bind(this);
    this.setQueues = this.setQueues.bind(this);
    this.handleChangeEvent = this.handleChangeEvent.bind(this);
}

/*
handleChangeEvent(target) {
    this.setState({ [target.id]: target.value });
}
*/


//Added by JR for Save Function
handleChangeEvent(event){
    console.info("From handle change", event);
    this.setState({ name: event.target.value })
}

/*
handleSubmit(event) {
    event.preventDefault();
    this.props.login(this.state.workspaceSID, this.state.apiKey, this.state.apiSecret);
}
*/

handleSubmit(event) {
    event.preventDefault();
    var text = this.state.text;
    console.info("form testing value output:");
}


setCredentials(workspaceSID, apiKey, apiSecret) {
    this.setState({ workspaceSID: workspaceSID });
    this.setState({ apiKey: apiKey });
    this.setState({ apiSecret: apiSecret });
}

getCredentials() {
    return this.state;
}

setQueues(queues) {
    this.setState({ queues: queues });
}




getConfig(apiKey, apiSecret) {
    return axios.get(`https://flex-api.twilio.com/v1/Configuration`, {
        auth: {
            username: apiKey,
            password: apiSecret
        },
    });


    
}


//Added by JR used to post configuration 
postConfig(apiKey, apiSecret, params) {
    return axios.post(`https://flex-api.twilio.com/v1/Configuration`, {
        auth: {
            username: apiKey,
            password: apiSecret
        },
     
    });
}


listQueues(workspaceSID, apiKey, apiSecret) {
    this.setCredentials(workspaceSID, apiKey, apiSecret);
    const queuesPromise = axios.get(`https://taskrouter.twilio.com/v1/Workspaces/${workspaceSID}/TaskQueues?PageSize=1000&Page=0`, {
        auth: {
            username: apiKey,
            password: apiSecret
        },
    });
    const channelsPromise = axios.get(`https://taskrouter.twilio.com/v1/Workspaces/${workspaceSID}/TaskChannels?PageSize=1000&Page=0`, {
        auth: {
            username: apiKey,
            password: apiSecret
        },
    });


    const configPromise = this.getConfig(apiKey, apiSecret);
    

    Promise.all([queuesPromise, channelsPromise, configPromise])
        .then((values) => {
            console.info(values);
            const twilioQueues = values[0].data.task_queues;
            const twilioChannels = values[1].data.channels;
            // const config = values[2].data.queue_stats_configuration;

            const voice = twilioChannels.find(channel => {
                console.info(channel)
                return channel.unique_name === 'voice'
            });

            const chat = twilioChannels.find(channel => {
                console.info(channel)
                return channel.unique_name === 'chat'
            });

            const email = twilioChannels.find(channel => {
                console.info(channel)
                return channel.unique_name === 'email'
            });

            const channels = [voice, chat, email];
            const queues = twilioQueues.map(q => {
                const queue = {
                    queue_sid: q.sid,
                    friendly_name: q.friendly_name,
                    channels
                }
                return queue;
            });

            this.setQueues(queues);
        });
}
   //onChange = {this.handleInputChange.bind(this)}

render() {
    const { name } = this.state
    return (
        <div>
            <p> Test Value is : {name}  </p>
            <LoginForm login = {this.listQueues} buttonText='Load Queues' />
            
            <Editor onSubmit = {this.handleSubmit} onChange = {this.handleChangeEvent} data = {this.state.queues} credentials = {this.getCredentials} setData = {this.setQueues} />
        </div>
    );
}
}

Editor.js

import React from 'react';
import MaterialTable from 'material-table'
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import axios from 'axios';
import { waitForDomChange } from '@testing-library/react';

export default class Editor extends React.Component {
constructor(props) {
    super(props);
    this.state = {
        open: false,
        queue: {},
        columns: [
            { title: 'Queue Name', field: 'friendly_name', editable: 'never' },
            { title: 'Channel', field: 'channel_friendly_name', editable: 'never' },
            { title: 'Reset Timezone', field: 'channel_reset_timezone' },
            { title: 'Reset Time', field: 'channel_reset_time' },
            { title: 'Service Level Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
            { title: 'Short Abandoned Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
        ]
    }
    this.handleClose = this.handleClose.bind(this);
   // this.handleSave = this.handleSave.bind(this);

}




handleClose(xxx) {
    console.info('this happening');
    console.info(xxx);
    this.setState({ open: true });
}

/*
handleSave(event) {
    console.info('Saving user input data');
    console.info(event);

    this.setState({ value: event.target.value});
}
*/




render() {
    console.info('hey ho');
    console.group(this.props.data);
    const queues = this.props.data.map(q => {
        const channel = q.channels.find(c => c.unique_name === 'voice');
        q.channel_friendly_name = channel.friendly_name;
        q.channel_reset_time = channel.reset_time;
        q.channel_reset_timezone = channel.reset_timezone;
        q.channel_service_level_threshold = channel.service_level_threshold;
        q.channel_service_level_threshold = channel.service_level_threshold;

        return q;


    })
    return (
        <div>
            <MaterialTable
                title='Queue SLA'
                columns = {this.state.columns}
                data = {queues}
                options = {{
                    pageSize: 25,
                    pageSizeOptions: [25, 100, 500, 1000]
                }}
                detailPanel = {rowData => {

                    //Modified by JR for Save Function
                    return FormContainer(rowData, this.props.credentials().apiKey, 
this.props.credentials().apiSecret, this.handleChangeEvent);
                  
                }}
                
            />
        </div>
    );
}
}



function FormRow(data, handleChangeEvent) {
const queue = data.data;
const channels = queue.channels;



return (
    channels.map((channel, index) => (
        <Grid container item sm = {12} spacing = {3}>
            <Grid item sm = {2}>
                <TextField
                    autoFocus
                    margin = "dense"
                    id = "channel_name"
                    label = "Channel"
                    type = "text"
                    value = {channel.friendly_name}
                    fullWidth
                    disabled
                />
            </Grid>
            <Grid item sm = {2}>
                <TextField
                    autoFocus
                    margin = "dense"
                    id = "reset_timezone"
                    label = "Reset Timezone"
                    type = "text"
                    value = "GMT"
                    fullWidth
                    disabled
                />
            </Grid>

          
         

            <Grid item sm = {2}>
                <TextField
                    autoFocus
                    margin = "dense"
                    id = "service_level_threshold"
                    label = "Service Level Threshold"
                    value = {channel.service_level_threshold}
                    onChange = {handleChangeEvent}
                    type = "text"
                    fullWidth
                />
            </Grid>
            <Grid item sm = {2}>
                <TextField
                    autoFocus
                    margin = "dense"
                    id = "short_abandoned_threshold"
                    label = "Short Abandoned Threshold"
                    value = {channel.short_abandoned_threshold}
                    onChange = {handleChangeEvent}
                    type = "text"
                    fullWidth
                />
            </Grid>
        </Grid>
    ))
);
}


 //Modified by JR for Save Function
    function FormContainer(data, apiKey, apiSecret, props, handleChangeEvent) {
    console.info('what');
   console.info(data);
    console.info('JSON what');
    console.info(JSON.stringify(data));
    const queue = data;




  // const queue = data.data;
   const channels = queue.channels;


const cancel = () => {
    console.info('cancel');
}



const save = () => {

    //Modified by JR for Save Function
    console.info('save');
    console.info(data);

    console.info(JSON.stringify(data));

    console.info('SaveButtonClicked');
    waitForDomChange();

    console.info(data);

    console.info(JSON.stringify(data));
    savebuttonclicked(queue, apiKey, apiSecret);

    
    console.info('Props Information');
    console.info(props);
 



    console.info('Save Sucessful');

}
return (
    <div>
        <Grid container>
            <Grid item sm = {1}></Grid>
            <Grid container item sm = {11} spacing = {3}>
                <FormRow data = {queue}
                    formOnChange = {handleChangeEvent}/>
                
            </Grid>
            <Grid
                container
                direction = "row"
                justify = "flex-end"
                alignItems = "center"
            >
                <Grid item sm = {1}></Grid>
                <Grid item sm = {11} spacing = {3} justify = "flex-end"
                    alignItems = "center">


                   
                    <Grid item sm = {2}>
                        <TextField 
                            autoFocus
                            margin = "dense"
                            id = "reset_time"
                            label = "Queue Reset Time"
                            type = "text"
                            value = {channels.reset_time}
                            onChange = {handleChangeEvent}
                            fullWidth
                            
                        />
                    </Grid>

                    <Button variant = "outlined" onClick = {cancel} color = "secondary">Cancel</Button>
                    <Button variant = "outlined" onClick = {save} color = "primary">Save</Button>


                </Grid>
            </Grid>
        </Grid>
    </div>
);
}

//Add by JR for Save Function
function savebuttonclicked(data, apiKey, apiSecret) {

const workspace = workspaces[0];

console.info('Test');

alert(JSON.stringify(data));
console.info(JSON.stringify(data));


var params = [];

for (const [index, value] of data.channels.entries()) {
    params.push({
        'url': data.channels[index].url,
        'unique_name': data.channels[index].unique_name,
        'account_sid': data.channels[index].account_sid,
        'channel_reset_time': data.channels[index].channel_reset_time,
        'channel_reset_timezone': data.channels[index].channel_reset_timezone,
        'channel_service_level_threshold': data.channels[index].channel_service_level_threshold
    })
}

alert(JSON.stringify(params));
console.info('Parms for API post:');
console.info(JSON.stringify(params));

/* 
 * Loop for API call for each URL in created JSON
 * 
for (const [index, value] of params.entries())
 {
    axios.post(params[index].url, params[index], {
        auth: {
            username: apiKey,
            password: apiSecret,
        }
    })
}
*/

axios.get(workspace.apiURL, {
    auth: {
        username: apiKey,
        password: apiSecret,
    },
})

    .then(function (response) {
        alert('Save Sucessful.')
        alert(JSON.stringify(response))

        console.info('success');
        console.info(response.headers);
    })
    .catch(function (error) {
        alert('Error Occured.')
        alert(error)

        console.info('Error');
        console.info(error);
    });
 }

С примененным исправлением это новая ошибка:

TypeError: не удается прочитать свойства «каналов» неопределенного FormRow C:/Users/drago/Source/Repos/twilio-flex-editor/src/queues/Editor.js:99 96 | 97 | function FormRow({data, handleChangeEvent}) { 98 | постоянная очередь = данные.данные; > 99 | константные каналы = очередь.каналы; 100 | 101 | 102 | Просмотр скомпилированных ▶ 18 кадров стека были свернуты.

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
0
566
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Проблема

Вы определили FormRow для использования обратного вызова handleChangeEvent, а также неправильно определили свойства. Они должны быть деструктурированы из одного объекта реквизита.

function FormRow(data, handleChangeEvent) {...

но передали обратный вызов formOnChange prop

<FormRow data = {queue} formOnChange = {handleChangeEvent} />

Вы также передаете this.handleChangeEvent в Editor, но никогда не потребляете его

<Editor
  onSubmit = {this.handleSubmit}
  onChange = {this.handleChangeEvent}
  data = {this.state.queues}
  credentials = {this.getCredentials}
  setData = {this.setQueues}
/>

и (возможно, по совпадению) передать неопределенную this.handleChangeEvent функцию FormContainer, которая передается FormRow компоненту.

Решение

Исправьте определение компонента FormRow

function FormRow({ data, handleChangeEvent }) {...

Передайте handleChangeEvent на FormRow как handleChangeEvent.

<FormRow data = {queue} handleChangeEvent = {handleChangeEvent} />

Либо определите this.handleChangeEvent в Editor для передачи, либо передайте this.props.onChange дальше.

return FormContainer(
  rowData,
  this.props.credentials().apiKey,
  this.props.credentials().apiSecret,
  this.props.onChange,
);

Спасибо, я внес изменения, рекомендованные выше, но теперь получил следующую ошибку: TypeError: Cannot read property 'channels' undefined FormRow C:/Users/drago/Source/Repos/twilio-flex-editor/src/queues/Ed‌​ itor.js:99 96 | 97 | function FormRow({data, handleChangeEvent}) { 98 | постоянная очередь = данные.данные; > 99 | константные каналы = очередь.каналы; 100 | 101 | 102 | Просмотр скомпилированных ▶ 18 кадров стека были свернуты.

Hun73r3006 16.12.2020 16:29

@ Hun73r3006 Hun73r3006 В вашем предыдущем коде data обрабатывался как ваш объект «реквизит», а handleChangeEvent был неопределенным, поэтому data.data работал. Это было синонимом props.data. С моим вышеуказанным исправлением data уже деструктурирован из props, поэтому вы можете просто деструктурировать queue из data, const { queue } = data;, теперь data.queue.channels или queue.channels будут работать.

Drew Reese 16.12.2020 20:02

Отлично, это сработало, я все еще обращался к нему через Data.data. Спасибо. С точки зрения сохранения данных обратно в данные и передачи новых входных данных в функцию SaveButtonClicked, создайте контейнер в очереди и передайте их? Я тестировал что-то подобное, и данные не обновляются.

Hun73r3006 17.12.2020 16:33

@ Hun73r3006 Hun73r3006 Этот дополнительный вопрос может быть немного не по теме этого сообщения SO, вам следует задать новый вопрос с подробностями. Похоже, ваша функция save просто вызывает savebuttonclicked и передает любое текущее значение реквизита/аргумента data/queue. ТБХ, я не совсем понимаю, о чем вы спрашиваете.

Drew Reese 17.12.2020 18:05

Это изображение ошибки, которую я получил: введите здесь описание изображения

Другие вопросы по теме