【问题标题】:Can't get onChange function to work on React with Grid Containers无法让 onChange 函数在使用网格容器的 React 上工作
【发布时间】:2021-03-26 22:46:09
【问题描述】:

尝试为 FormRow 函数设置 onChange 事件处理程序时出现图示错误。最终需要存储来自 Que Reset Time、Service Level threshold 和 Short Abandoned Threshold 的用户数据。 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.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:无法读取未定义 FormRow 的属性“通道” C:/Users/drago/Source/Repos/twilio-flex-editor/src/queues/Editor.js:99 96 | 97 |函数FormRow({数据,handleChange}){98 |常量队列 =数据。数据; > 99 |常量频道 = queue.channels; 100 | 101 | 102 |查看编译 ▶ 18 个堆栈帧被折叠。

【问题讨论】:

    标签: javascript reactjs grid onchange


    【解决方案1】:

    问题

    您已定义 FormRow 以使用 handleChange 回调,并且还错误地定义了道具。它们应该从单个 props 对象中解构出来。

    function FormRow(data, handleChange) {...
    

    但已通过formOnChange prop 回调

    <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 传递给FormRow 作为handleChange

    <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,
    );
    

    【讨论】:

    • 谢谢,我做了上面建议的更改,但现在收到以下错误:TypeError: Cannot read property 'channels' of undefined FormRow C:/Users/drago/Source/Repos/twilio-flex-编辑器/src/queues/Editor.js:99 96 | 97 |函数FormRow({数据,handleChange}){98 |常量队列 = data.data; > 99 |常量频道 = queue.channels; 100 | 101 | 102 |查看编译 ▶ 18 个堆栈帧被折叠。
    • @Hun73r3006 在您之前的代码中,data 被视为您的“道具”对象,而handleChange 未定义,这就是data.data 起作用的原因。它是props.data 的同义词。通过我上面的修复,data 已经被 props 解构,所以你可以简单地从dataconst { queue } = data; 解构queue,现在data.queue.channelsqueue.channels 将工作。
    • 太好了,我仍然可以通过 Data.data 访问它。谢谢你。在将数据存储回数据并将新输入传递给 SaveButtonClicked 函数方面,创建队列容器并传递那些?我测试了类似的东西,但数据没有更新。
    • @Hun73r3006 这个后续问题对于 this SO 帖子可能有点偏离主题,您应该提出一个新问题并提供详细信息。看起来您的 save 函数只是调用 savebuttonclicked 并传递当前的 data/queue 属性/参数值。 TBH 我不太明白你在问什么。
    【解决方案2】:

    这是我收到的错误的图片: enter image description here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-21
      • 1970-01-01
      • 1970-01-01
      • 2021-06-30
      • 2016-02-11
      • 1970-01-01
      • 2021-08-11
      相关资源
      最近更新 更多