无法获得 onChange 函数以使用 Grid Container 进行 React
Can't get onChange function to work on React with Grid Containers
尝试为 FormRow 函数设置 onChange 事件处理程序时出现如图所示的错误。最终需要存储来自Que Reset Time、Service Level threshold和Short Abandoned Threshold的用户数据。 Queuecontainer 是使用编辑器创建容器表单的主要 class。
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.handleChange = this.handleChange.bind(this);
}
/*
handleChange(target) {
this.setState({ [target.id]: target.value });
}
*/
//Added by JR for Save Function
handleChange(event){
console.log("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.log("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.log(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.log(channel)
return channel.unique_name === 'voice'
});
const chat = twilioChannels.find(channel => {
console.log(channel)
return channel.unique_name === 'chat'
});
const email = twilioChannels.find(channel => {
console.log(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.handleChange} 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.log('this happening');
console.log(xxx);
this.setState({ open: true });
}
/*
handleSave(event) {
console.log('Saving user input data');
console.log(event);
this.setState({ value: event.target.value});
}
*/
render() {
console.log('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.handleChange);
}}
/>
</div>
);
}
}
function FormRow(data, handleChange) {
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={handleChange}
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={handleChange}
type="text"
fullWidth
/>
</Grid>
</Grid>
))
);
}
//Modified by JR for Save Function
function FormContainer(data, apiKey, apiSecret, props, handleChange) {
console.log('what');
console.log(data);
console.log('JSON what');
console.log(JSON.stringify(data));
const queue = data;
// const queue = data.data;
const channels = queue.channels;
const cancel = () => {
console.log('cancel');
}
const save = () => {
//Modified by JR for Save Function
console.log('save');
console.log(data);
console.log(JSON.stringify(data));
console.log('SaveButtonClicked');
waitForDomChange();
console.log(data);
console.log(JSON.stringify(data));
savebuttonclicked(queue, apiKey, apiSecret);
console.log('Props Information');
console.log(props);
console.log('Save Sucessful');
}
return (
<div>
<Grid container>
<Grid item sm={1}></Grid>
<Grid container item sm={11} spacing={3}>
<FormRow data={queue}
formOnChange={handleChange}/>
</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={handleChange}
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.log('Test');
alert(JSON.stringify(data));
console.log(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.log('Parms for API post:');
console.log(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.log('success');
console.log(response.headers);
})
.catch(function (error) {
alert('Error Occured.')
alert(error)
console.log('Error');
console.log(error);
});
}
应用修复后,这是一个新错误:
TypeError: Cannot read property 'channels' of undefined FormRow
C:/Users/drago/Source/Repos/twilio-flex-editor/src/queues/Editor.js:99
96 | 97 | function FormRow({ data, handleChange }) { 98 | const queue
= data.data; > 99 | const channels = queue.channels; 100 | 101 | 102 | View compiled ▶ 18 stack frames were collapsed.
问题
您已将 FormRow
定义为使用 handleChange
回调,并且还错误地定义了道具。它们应该从单个道具对象中解构。
function FormRow(data, handleChange) {...
但已经通过了 formOnChange
道具回调
<FormRow data={queue} formOnChange={handleChange} />
您也将 this.handleChange
传递给 Editor
但从未使用它
<Editor
onSubmit={this.handleSubmit}
onChange={this.handleChange}
data={this.state.queues}
credentials={this.getCredentials}
setData={this.setQueues}
/>
并且(可能巧合地)将未定义的 this.handleChange
函数传递给 FormContainer
,后者传递给 FormRow
组件。
解决方案
修复 FormRow
组件定义
function FormRow({ data, handleChange }) {...
将 handleChange
作为 handleChange
传递给 FormRow
。
<FormRow data={queue} handleChange={handleChange} />
要么在Editor
中定义this.handleChange
传递,要么在this.props.onChange
上传递。
return FormContainer(
rowData,
this.props.credentials().apiKey,
this.props.credentials().apiSecret,
this.props.onChange,
);
这是我收到的错误图片:
enter image description here
尝试为 FormRow 函数设置 onChange 事件处理程序时出现如图所示的错误。最终需要存储来自Que Reset Time、Service Level threshold和Short Abandoned Threshold的用户数据。 Queuecontainer 是使用编辑器创建容器表单的主要 class。
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.handleChange = this.handleChange.bind(this);
}
/*
handleChange(target) {
this.setState({ [target.id]: target.value });
}
*/
//Added by JR for Save Function
handleChange(event){
console.log("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.log("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.log(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.log(channel)
return channel.unique_name === 'voice'
});
const chat = twilioChannels.find(channel => {
console.log(channel)
return channel.unique_name === 'chat'
});
const email = twilioChannels.find(channel => {
console.log(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.handleChange} 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.log('this happening');
console.log(xxx);
this.setState({ open: true });
}
/*
handleSave(event) {
console.log('Saving user input data');
console.log(event);
this.setState({ value: event.target.value});
}
*/
render() {
console.log('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.handleChange);
}}
/>
</div>
);
}
}
function FormRow(data, handleChange) {
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={handleChange}
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={handleChange}
type="text"
fullWidth
/>
</Grid>
</Grid>
))
);
}
//Modified by JR for Save Function
function FormContainer(data, apiKey, apiSecret, props, handleChange) {
console.log('what');
console.log(data);
console.log('JSON what');
console.log(JSON.stringify(data));
const queue = data;
// const queue = data.data;
const channels = queue.channels;
const cancel = () => {
console.log('cancel');
}
const save = () => {
//Modified by JR for Save Function
console.log('save');
console.log(data);
console.log(JSON.stringify(data));
console.log('SaveButtonClicked');
waitForDomChange();
console.log(data);
console.log(JSON.stringify(data));
savebuttonclicked(queue, apiKey, apiSecret);
console.log('Props Information');
console.log(props);
console.log('Save Sucessful');
}
return (
<div>
<Grid container>
<Grid item sm={1}></Grid>
<Grid container item sm={11} spacing={3}>
<FormRow data={queue}
formOnChange={handleChange}/>
</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={handleChange}
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.log('Test');
alert(JSON.stringify(data));
console.log(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.log('Parms for API post:');
console.log(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.log('success');
console.log(response.headers);
})
.catch(function (error) {
alert('Error Occured.')
alert(error)
console.log('Error');
console.log(error);
});
}
应用修复后,这是一个新错误:
TypeError: Cannot read property 'channels' of undefined FormRow C:/Users/drago/Source/Repos/twilio-flex-editor/src/queues/Editor.js:99 96 | 97 | function FormRow({ data, handleChange }) { 98 | const queue = data.data; > 99 | const channels = queue.channels; 100 | 101 | 102 | View compiled ▶ 18 stack frames were collapsed.
问题
您已将 FormRow
定义为使用 handleChange
回调,并且还错误地定义了道具。它们应该从单个道具对象中解构。
function FormRow(data, handleChange) {...
但已经通过了 formOnChange
道具回调
<FormRow data={queue} formOnChange={handleChange} />
您也将 this.handleChange
传递给 Editor
但从未使用它
<Editor
onSubmit={this.handleSubmit}
onChange={this.handleChange}
data={this.state.queues}
credentials={this.getCredentials}
setData={this.setQueues}
/>
并且(可能巧合地)将未定义的 this.handleChange
函数传递给 FormContainer
,后者传递给 FormRow
组件。
解决方案
修复 FormRow
组件定义
function FormRow({ data, handleChange }) {...
将 handleChange
作为 handleChange
传递给 FormRow
。
<FormRow data={queue} handleChange={handleChange} />
要么在Editor
中定义this.handleChange
传递,要么在this.props.onChange
上传递。
return FormContainer(
rowData,
this.props.credentials().apiKey,
this.props.credentials().apiSecret,
this.props.onChange,
);
这是我收到的错误图片: enter image description here