ReactJS Antd Table Rendering Old Data - reactjs

Summary: I have a component that contains an Antd Table component. The Antd Table component is created initially based on the original userData that is generated using a fetch request. The data is loaded initially and set using setState(fetchedData) calling render() to display appropriately. This works.
Using the NiceModal library, I present a new modal component and pass in a doCompelte closure that will then setstate again with the updated data. From here, the render() method is called again. I log the updated userData in the render() method before the data is loaded and it shows the data is updated and it confirms that render() is called with the new data. But, the visual display never actually loads on the screen.
At this point, I stopped taking user input from the Modal and tried giving it direct values, as shown in EditModal.jsx. This still doesn't work and I'm unsure of why.
Question: Why is my component render() method called, new data printed correctly, but the React Antd Table is not loading the new data?
Note: I did try creating a local variable inside the render() method that accesses userData at that given time but this also doesn't update the table.
UPDATE 1: You can see that the first name is "Mrs." then it gets hardcode changed to "GPRLEJAF" inside the showEditModal() function which now does NOT show a modal, it just changes the value and sets the state.
You'll see that the re-rendering shows the update in the console but the table is never updated.
UserList.jsx
import React, { useEffect } from 'react';
import NiceModal, { useModal } from '#ebay/nice-modal-react';
import { Button, Table } from 'antd';
import { EditOutlined } from '#ant-design/icons';
import AddCircle from '../../../node_modules/material-jsx-icons/dist/icons/add_circle';
import { useLocation } from 'react-router-dom';
import AddModal from './AddModal'
import EditModal from './EditModal'
import "./Table.css";
import 'bootstrap/dist/css/bootstrap.min.css';
import data from '../../mock';
function search() {
}
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useLocation();
return <Component {...props} myHookValue={myHookValue} />;
}
}
class UserList extends React.Component {
constructor(props) {
super(props);
this.state = {
userData: [],
}
}
self = this;
columns = [
{
title: 'First Name',
dataIndex: 'First_Name',
width: '10%',
align: 'center',
},
{
title: 'Last Name',
dataIndex: 'Last_Name',
width: '10%',
align: 'center',
},
{
title: 'Address',
dataIndex: 'Address',
width: '20%',
align: 'center',
},
{
title: 'Billed',
dataIndex: 'Billed',
width: '5%',
align: 'center',
},
{
title: 'Work Date',
dataIndex: 'Work_Date',
width: '12%',
align: 'center',
},
{
title: 'Last Modified Date',
dataIndex: 'Table_Date',
width: '12%',
align: 'center',
},
{
title: 'Notes',
dataIndex: 'Notes',
width: '32%',
align: 'center',
},
{
title: 'Actions',
width: '5%',
align: 'center',
dataIndex: 'taco',
render: (_, value) => <Button type="link" icon={<EditOutlined />} onClick={() => this.showEditModal(value)}/>
},
];
showEditModal(value) {
/*
var index = this.userData.findIndex(item => item == value);
NiceModal.show(EditModal, { data: value, onEdit: (updatedUser) => {
this.userData[index] = updatedUser; // Update current user with updated data
this.setState([...this.userData]); // Re-Render with new data
}});
*/
this.userData[0].First_Name = "Random Name";
this.setState([this.userData]);
};
iconStyles = {
width: '25px',
fill: '#ffffff',
justifyContent: 'center',
alignItems: 'center',
float: 'left',
marginRight: '5px',
};
componentDidMount() {
console.log("Updating");
const token = this.props.myHookValue.state['token'];
console.log("Token", this.props.myHookValue.state['token']);
let response = fetch('https://x7n8z7pgo5.execute-api.us-east-1.amazonaws.com/dev', {
headers: {
"Authorization": token
}
})
.then(response => response.json())
.then(data => {
console.log("Retrieved Data", data);
this.userData = data["Items"];
console.log("User Data", this.userData);
this.setState([...data["Items"]]); //re-renders data
})
.catch(err => console.log(err));
}
showAddModal = () => {
NiceModal.show(AddModal, { component: this });;
};
render() {
const curData = this.userData;
console.log("Rerendering", curData);
return <div>
<div className="container-xl">
<div className="table-responsive">
<div className="table-wrapper">
<div className="table-title">
<div className="row">
<div className="col-sm-6">
<h2>Manage Clients</h2>
</div>
<div className="col-sm-6">
{<Button className="btn btn-success" type="primary" onClick={this.showAddModal}>
<AddCircle style={this.iconStyles}/> Add New Client
</Button>}
</div>
</div>
</div>
<input id="search" type="text" placeholder="Search..." onKeyUp={search}/>
<Table
size="small"
rowKey="id"
pagination={false}
columns={this.columns}
dataSource={curData}
style={{ marginTop: '20px' }}
/>
</div>
</div>
<div>
</div>
</div>
</div>
}
}
export default withMyHook(UserList);
EditModal.jsx
import { Modal } from 'antd';
import NiceModal, { useModal } from '#ebay/nice-modal-react';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogContentText from '#material-ui/core/DialogContentText';
import DialogTitle from '#material-ui/core/DialogTitle';
import { FilePicker, TextInput } from 'evergreen-ui';
import TextArea from 'antd/lib/input/TextArea';
import TextField from '#material-ui/core/TextField';
import { Button } from 'antd';
import React, { useState } from 'react';
function textInputStyles(height='40px') {
return {
width: '350px',
fontSize: '20px',
height: height,
}
}
export default NiceModal.create(({ data, onEdit }) => {
const user = data;
const [firstName, setFirstName] = useState(user.First_Name);
const [lastName, setLastName] = useState(user.Last_Name);
const [address, setAddress] = useState(user.Address);
const [billed, setBilled] = useState(user.Billed);
const [workDate, setWorkDate] = useState(user.Work_Date);
const [notes, setNotes] = useState(user.Notes);
const modal = useModal();
return (
<Dialog
open={modal.visible}
onClose={() => {
modal.hide();
}}
TransitionProps={{
onExited: () => modal.remove(),
}}
>
<DialogTitle id="edit">{"Edit Client"}</DialogTitle><hr></hr>
<DialogContent>
<DialogContentText id="edit_first_name">
First Name
</DialogContentText>
<DialogContentText>
<TextInput
style={textInputStyles()}
defaultValue={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
</DialogContentText>
<DialogContentText id="edit_last_name">
Last Name
</DialogContentText>
<DialogContentText>
<TextInput
style={textInputStyles()}
defaultValue={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
</DialogContentText>
<DialogContentText id="edit_address">
Address
</DialogContentText>
<DialogContentText>
<TextInput
style={textInputStyles()}
defaultValue={address}
onChange={(e) => setAddress(e)}
/>
</DialogContentText>
<DialogContentText id="edit_billed">
Billed
</DialogContentText>
<DialogContentText>
<TextInput
style={textInputStyles()}
defaultValue={billed}
/>
</DialogContentText>
<DialogContentText id="edit_work_date">
Work Date
</DialogContentText>
<DialogContentText>
<TextField
id="date"
label=""
type="date"
InputLabelProps={{
shrink: true,
}}
defaultValue={new Date(workDate).toISOString().substring(0, 10)}
/>
</DialogContentText>
<DialogContentText id="edit_last_modified_date">
Today's Date
</DialogContentText>
<DialogContentText>
<TextInput
style={textInputStyles()}
value={new Date().toDateString().split(' ').slice(1).join(' ')}
editable={false}
/>
</DialogContentText>
<DialogContentText id="edit_notes">
Notes
</DialogContentText>
<DialogContentText>
<TextArea
style={textInputStyles(175)}
editable={false}
defaultValue={notes}
/>
</DialogContentText>
<DialogContentText>
Invoice
</DialogContentText>
<DialogContentText>
<FilePicker>
onClick={}
</FilePicker>
</DialogContentText>
</DialogContent>
<DialogActions style={{backgroundColor:'#D3D3D3', justifyContent: 'space-between'}}>
<Button onClick={() => modal.hide()} className="btn" style={{backgroundColor:'red', width: '25%'}}>
Cancel
</Button>
<Button onClick={() => {
modal.hide();
const updatedUser = {
'Address': 'asd',
'First_Name': 'asd',
'Billed': '123',
'Index': user.Index,
'Last_Name': 'fadsad',
'Notes': 'sdad',
'Table_Date': new Date(workDate).toISOString().substring(0, 10),
'Work_Date': workDate
};
onEdit(updatedUser);
}} className="btn" style={{backgroundColor: '#47b300', color:'white', width: '25%', float:'right'}}>
Add
</Button>
</DialogActions>
</Dialog>
);
});

Maybe here in componentDidMount and ShowEditModal (but we don't the server response format) in UserList component
componentDidMount() {
console.log("Updating");
const token = this.props.myHookValue.state['token'];
console.log("Token", this.props.myHookValue.state['token']);
let response = fetch('https://x7n8z7pgo5.execute-api.us-east-1.amazonaws.com/dev', {
headers: {
"Authorization": token
}
})
.then(response => response.json())
.then(data => {
console.log("Retrieved Data", data);
this.userData = data["Items"];
console.log("User Data", this.userData);
//this.setState([...data["Items"]]); //re-renders data
this.setState({userData: data["Items"]}); //re-renders data
//or
this.setState({...data["Items"]});
})
.catch(err => console.log(err));
}
showEditModal(value) {
var index = this.userData.findIndex(item => item == value);
NiceModal.show(EditModal, { data: value, onEdit: (updatedUser) => {
this.userData[index] = updatedUser; // Update current user with updated data
//this.setState([...this.userData]); // Re-Render with new data
this.setState({userData: this.userData});
//or
this.setState({...this.userData});
}});
};

Antd Tables require a copy of the data to be able to dynamically update the table.
render() {
if (typeof this.userData !== 'undefined') {
var copy = JSON.parse(JSON.stringify(this.userData));
console.log(copy);
return <div>
<div className="container-xl">
<div className="table-responsive">
<div className="table-wrapper">
<div className="table-title">
<div className="row">
<div className="col-sm-6">
<h2>Manage Clients</h2>
</div>
<div className="col-sm-6">
{<Button className="btn btn-success" type="primary" onClick={this.showAddModal}>
<AddCircle style={this.iconStyles}/> Add New Client
</Button>}
</div>
</div>
</div>
<input id="search" type="text" placeholder="Search..." onKeyUp={search}/>
<Table
size="small"
rowKey="id"
pagination={false}
columns={this.columns}
dataSource={copy}
style={{ marginTop: '20px' }}
/>
</div>
</div>
<div>
</div>
</div>
</div>
}
return <div>
Loading
</div>
}

Related

Passing value of component in separate jsx file's value to event on Full-Calendar

I am new to React. I am trying to pass the value of a select box imported from a separate .jsx file to an event for full-calendar. I can't find anything on this or figure out how to do it for the life of me. This is my component for the select box in a separate file.
import React from 'react'
export const Dropdown = (props) => (
<div className="form-group">
<select
className="form-control"
name="partner"
onChange={props.onChange}
>
<option defaultValue>Select Partner</option>
{props.options.map((item, index) => (
<option key={index} value={item.value}>
{item.label}
</option>
))}
</select>
</div>
)
export default class DropdownList extends React.Component {
constructor() {
super()
this.state = {
list: [],
chosenValue: '',
}
}
componentDidMount() {
fetch('/calendar/users')
.then((response) => response.json())
.then((item) => this.setState({ list: item }))
}
onDropdownChange = (e) => {
this.setState({ chosenValue: e.target.value })
}
render() {
return (
<div className="form-group">
<Dropdown
name="partner"
id="partner"
options={this.state.list}
onDropdownChange={this.onDropdownChange}
/>
</div>
)
}
}
This is my modal that contains the form to pass through as an event to full-calendar
import React, {useState} from "react";
import Modal from "react-modal";
import DateTimePicker from "react-datetime-picker";
import 'bootstrap-icons/font/bootstrap-icons.css'
import DropdownList from "./SelectBox";
export default function ({ isOpen, onClose, onEventAdded }) {
const [title, setTitle] = useState("");
const [start, setStart] = useState(new Date());
const [end, setEnd] = useState(new Date());
const [selectValue] = useState(DropdownList);
const onSubmit = (event) => {
event.preventDefault();
onEventAdded({
title,
start,
partner: selectValue,
end
});
onClose()
};
return (
<Modal isOpen={isOpen}
onClose={onClose}
style={{
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.5)'
},
content: {
top: '50%',
left: '50%',
right: 'auto',
bottom: 'auto',
marginRight: '-50%',
transform: 'translate(-50%, -50%)',
border: '1px solid #ccc',
background: '#fff',
overflow: 'auto',
WebkitOverflowScrolling: 'touch',
borderRadius: '4px',
outline: 'none',
padding: '20px',
maxWidth: '1200px',
minHeight: '80%',
textAlign: 'center'
}
}}
>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch#4.5.2/dist/lux/bootstrap.min.css" />
<form onSubmit={onSubmit}>
<div className='form-group'>
<input className='form-control' placeholder="Title" value={title} onChange={e => setTitle(e.target.value)} />
</div>
<DropdownList />
<div className='form-group'>
<DateTimePicker name='start' value={start} onChange={date => setStart(date)} />
</div>
<div className='form-group'>
<DateTimePicker name='end' value={end} onChange={date =>setEnd(date)} />
</div>
<button className="btn btn-primary btn-block" type="submit">Add Event</button>
<button className="btn btn-danger btn-block" onClick={onClose}>Cancel</button>
</form>
</Modal>
)
}
I have tried DropdownList.value, redoing the onDropdownChange(e) function in the modal jsx file to no avail. I'm sure this is really simple and I'm getting a headache so thank you for your kindness ahead of time. I'm currently trying to use props but it's all coming up undefined.
Edit: tried to add to component in the modal file
const [partner, setPartner] = useState();
<DropdownList
ref={partnerRef}
onDropdownChange={e => setPartner(this.chosenValue = e.target.value)}
/>

How can I call scrollIntoView on an ref in React

When I call someFun, I want to make formRef visible on the screen(it is on the top of the page, I may have scrolled to the bottom when I call someFun), is there standart way to do that?
function List(props) {
const formRef = useRef(null);
const someFun = params => {
if (formRef && formRef.current) {
//error FormRef.current.scrollIntoView is not a function
formRef.current.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'start' });
}
}
}
This is the form component used in the above parent component:
it is a ant design form component:
the link is here
https://3x.ant.design/components/form/#header
import { Form, Icon, Input, Button, Checkbox } from 'antd';
class NormalLoginForm extends React.Component {
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values);
}
});
};
render() {
const { getFieldDecorator } = this.props.form;
return (
<Form onSubmit={this.handleSubmit} className="login-form">
<Form.Item>
{getFieldDecorator('username', {
rules: [{ required: true, message: 'Please input your username!' }],
})(
<Input
prefix={<Icon type="user" style={{ color: 'rgba(0,0,0,.25)' }} />}
placeholder="Username"
/>,
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('password', {
rules: [{ required: true, message: 'Please input your Password!' }],
})(
<Input
prefix={<Icon type="lock" style={{ color: 'rgba(0,0,0,.25)' }} />}
type="password"
placeholder="Password"
/>,
)}
</Form.Item>
<Form.Item>
{getFieldDecorator('remember', {
valuePropName: 'checked',
initialValue: true,
})(<Checkbox>Remember me</Checkbox>)}
<a className="login-form-forgot" href="">
Forgot password
</a>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
Or register now!
</Form.Item>
</Form>
);
}
}
const WrappedNormalLoginForm = Form.create({ name: 'normal_login' })(NormalLoginForm);
ReactDOM.render(<WrappedNormalLoginForm />, mountNode);
This how you can pass the ref from your NormalLoginForm to your List component.
In your List component you need to use forwardRef because it gives the child component a reference to a DOM element created by its parent component
Example
App
import "./styles.css";
import List from "./List";
import { useRef } from "react";
export default function App() {
const targetElement = useRef();
const scrollingTop = (event) => {
const elmnt = targetElement;
elmnt.current.scrollIntoView({
behavior: "smooth",
block: "center",
inline: "start"
});
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<p>lorem ipsum </p>
<div style={{ height: "200vh", backgroundColor: "orange" }}>
<h1>Example Form Tag </h1>
<button id="btnAppear" onClick={scrollingTop}>
Submit Scroll bottom
</button>
</div>
<List ref={targetElement} />
</div>
);
}
List
import { forwardRef } from "react";
const List = (ref) => {
return (
<div
style={{
backgroundColor: "purple",
paddingTop: "50px",
color: "white"
}}
ref={ref}
>
<h1>List View</h1>
</div>
);
};
export default forwardRef(List);
DEMO

React and Antd, form data destroys on fulllscreen drawer out of focus

I have tried to remove the destroy on close, use same form hook, different form hook, removed from hook, but nothing worked.
I have consoled logged the form data on show modal and close modal. Show modal has the form data but inside the log of close modal the data is reset to undefined. As soon as the modal opens the Data resets to null.
In the following code you can see the creation of Full screen modal which is made using AntDrawer:
import React, { useEffect, useState } from "react";
import FullscreenDialog from "Components/FullScreenDialog";
import Form from "Components/Form";
import { Input, Typography, Button, Space, Menu, Dropdown } from "antd";
import { PlusCircleOutlined, CloseOutlined, DownOutlined } from "#ant-design/icons";
import HeaderInput from "Components/Inputs/HeaderInput";
import Table from "Components/Table";
import styled from "styled-components";
import { Services } from "Utilities/network";
import DetailsModal from "./DetailsModal";
const RootWrapper = styled.div`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
`;
const CreateVoucher = ({ visible, initalValue = {}, onSuccess = (e) => e, onClose = (e) => e }) => {
const [form] = Form.useForm();
const [voucherData, setVoucherData] = useState([]);
const [newModal, setNewModal] = useState(false);
const [formData, setFormData] = useState({});
const handleSubmit = async () => {
const { _id, name, header } = form.getFieldsValue(true);
if (name && header && voucherData) {
let resp = await Services.createVoucher({
_id,
name,
header,
voucherData,
});
if (resp.key) onSuccess();
}
};
if (initalValue) {
const { _id, name, header, voucherData } = initalValue;
form.setFieldsValue({
_id,
name,
header,
voucherData,
});
}
useEffect(() => {
const { voucherData } = initalValue;
if (voucherData && voucherData.length) setVoucherData(voucherData);
else setVoucherData([]);
}, [initalValue]);
const handleDelete = (index) => {
const tableData = voucherData.filter((data, dataIndex) => {
if (index !== dataIndex) return data;
return null;
});
setVoucherData(tableData);
};
return (
<>
<FullscreenDialog visible={visible} onClose={onClose}>
<FullscreenDialog.Paper>
<RootWrapper>
<Typography.Text strong style={{ fontSize: "30px" }}>
Generate Voucher
</Typography.Text>
<Form form={form}>
<Space direction="vertical" style={{ width: "100%" }}>
<Form.Item label="Voucher ID" name="name" rules={[{ required: true, message: "Please input ID" }]}>
<Input />
</Form.Item>
<HeaderInput
fieldProps={{
name: "header",
label: "Inoivce Header",
rules: [{ required: true, message: "Please select header" }],
}}
itemProps={{ placeholder: "Select Header for Voucher" }}
/>
<Space style={{ float: "right", marginTop: "20px" }}>
<Button
type="primary"
shape="round"
icon={<PlusCircleOutlined />}
onClick={() => {
setFormData(form.getFieldsValue(true));
setNewModal(true);
}}
>
Add Details
</Button>
</Space>
<Table
dataSource={voucherData}
count={voucherData?.length || 0}
columns={[
{
title: "Label",
key: "label",
dataIndex: "label",
render: (_, record) => record.label,
},
{
title: "Details",
key: "detail",
dataIndex: "detail",
render: (_, record) => record.detail,
},
{
align: "right",
render: (_, record, index) => {
return (
<Dropdown
trigger={["click"]}
overlay={
<Menu>
<Menu.Item
style={{ color: "red " }}
onClick={() => {
handleDelete(index);
}}
>
<CloseOutlined /> Delete
</Menu.Item>
</Menu>
}
>
<Button type="primary">
Edit
<DownOutlined />
</Button>
</Dropdown>
);
},
},
]}
/>
<Space style={{ float: "right", marginTop: "30px" }}>
<Button type="outline" onClick={onClose}>
Cancel
</Button>
<Button type="primary" htmlType="submit" onClick={handleSubmit}>
Submit
</Button>
</Space>
</Space>
</Form>
</RootWrapper>
</FullscreenDialog.Paper>
</FullscreenDialog>
<DetailsModal
visible={newModal}
onClose={() => {
setNewModal(false);
console.log(formData);
form.setFieldsValue({ name: "2222" });
console.log(form.getFieldsValue(true));
}}
onSuccess={(data) => {
setVoucherData([...voucherData, data]);
setNewModal(false);
form.setFieldsValue(formData);
}}
/>
</>
);
};
export default CreateVoucher;
Below is the code for modal to use another form:
import React from "react";
import { Modal, Space, Input, Button } from "antd";
import Form from "Components/Form";
import styled from "styled-components";
const RootWrapper = styled.div`
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
`;
const DetailsModal = ({ visible, onClose = (e) => e, onSuccess = (e) => e }) => {
const [form] = Form.useForm();
return (
<Modal
visible={visible}
destroyOnClose={true}
onClose={() => {
form.resetFields();
onClose();
}}
onCancel={() => {
form.resetFields();
onClose(false);
}}
title="Add Details"
footer={false}
>
<Form form={form}>
<Space direction="vertical" style={{ width: "100%" }}>
<RootWrapper>
<Form.Item name="label" label="Label" rules={[{ required: true, message: "Please input label" }]}>
<Input />
</Form.Item>
<Form.Item name="detail" label="Details" rules={[{ required: true, message: "Please input details" }]}>
<Input />
</Form.Item>
</RootWrapper>
<Space style={{ float: "right", marginTop: "20px" }}>
<Button
type="outline"
onClick={() => {
form.resetFields();
onClose();
}}
>
Cancel
</Button>
<Button
type="primary"
htmlType="submit"
onClick={() => {
const { detail, label } = form.getFieldsValue(true);
onSuccess({ detail, label });
}}
>
Submit
</Button>
</Space>
</Space>
</Form>
</Modal>
);
};
export default DetailsModal;

Why my props are undefined when sending to components?

I have a table in my project and I have an edit / view / add page that I can access from this table. My goal is to send the clicked data to the other component without any problem, but no matter how hard I try, I get an undefined error and the project is broken. I would be glad if you could help.
I am sharing my codes from parent to child component
Table page.
import React, { useState, useEffect, useCallback, useMemo } from "react";
import ManagementTable from '../components/ManagementTable'
import {
getApps,
updateStopRisk,
countLiveCountry,
updateAppShow,
deleteApp,
} from "../api/apiCalls";
import VisibilityIcon from "#material-ui/icons/Visibility";
import DeleteIcon from "#material-ui/icons/Delete";
import EditIcon from "#material-ui/icons/Edit";
import Switch from "#material-ui/core/Switch";
import DeleteModal from "../components/DeleteModal";
import { Link } from "react-router-dom";
const Management = () => {
const [apps, setApps] = useState([]);
const [modalVisible, setModalVisible] = useState(false);
const [currentApp, setCurrentApp] = useState("");
const [appID, setAppID] = useState(0);
const fetchData = useCallback(async () => {
const { data: appsResponse } = await getApps();
const countLiveCountries = await fetchLiveCountriesForApps(appsResponse);
setApps(
appsResponse.map((app, idx) => ({
...app,
countLiveCountry: countLiveCountries[idx],
}))
);
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
const fetchLiveCountriesForApps = async (appwLive) => {
const countLiveCountries = await Promise.all(
appwLive.map((app) => countLiveCountry(app.appID))
);
return countLiveCountries.map(({ data: liveCountries }) => liveCountries);
};
const removeApp = async () => {
await deleteApp(appID);
setModalVisible(false);
fetchData();
};
const onClickCancel = () => {
setModalVisible(false);
};
const columns = useMemo(() => [
{
Header: "Application Name",
accessor: "app_name",
},
{
Header: "Business Area",
accessor: "businessarea.businessarea_name",
},
{
Header: "Live Plants",
accessor: "countLiveCountry",
},
{
Header: "Line Stop Risk",
accessor: "app_stoprisk",
Cell: ({ row: { original } }) => {
const changeCheck = async (id) => {
await updateStopRisk(id);
fetchData();
};
return (
<input
checked={original.app_stoprisk}
onClick={() => {
changeCheck(original.appID);
}}
id="appStopRisk"
type="checkbox"
style={{ width: 18, height: 18, marginTop: 5 }}
/>
)
},
sortType: (a, b, id) => {
if (a.original[id] > b.original[id]) return -1;
if (b.original[id] > a.original[id]) return 1;
},
},
{
Header: "Actions",
Cell: ({ row: { original } }) => {
const changeTrack = async (id) => {
await updateAppShow(id);
fetchData();
};
return (
<>
<Link
className="btn btn-manage-link btn-sm col-2"
to={{
pathname: `/management/${original.app_name}`,
mode: "view",
id: original.appID
}}
>
<VisibilityIcon></VisibilityIcon>
</Link>
<Link
to={{
pathname: `/management/${original.app_name}`,
mode: "edit",
id: original.appID
}}
className="btn btn-manage-link btn-sm col-2"
>
<EditIcon></EditIcon>
</Link>
<button
onClick={() => {
setModalVisible(true);
setCurrentApp(original.app_name);
setAppID(original.appID);
}}
className="btn btn-manage-link btn-sm col-3"
>
<DeleteIcon></DeleteIcon>
</button>
<Switch
onClick={() => changeTrack(original.appID)}
checked={original.app_show}
className="col-3"
></Switch>
</>
)
},
},
],
[fetchData]
);
return (
<div className="container">
<h2 style={{ float: "left", font: "bold" }}>Management</h2>
<div style={{ float: "right" }}>
<Link className="btn btn-danger btn-sm" to={{ pathname: `/management/add`, mode: "add" }}>
Add New App
</Link>
<Link className="btn btn-danger btn-sm ml-3" exact to="/management/plants">
Plant Management
</Link>
</div>
<ManagementTable columns={columns} data={apps} />
<DeleteModal
message={<strong>{currentApp}</strong>}
variety="app"
onClickCancel={onClickCancel}
onClickOk={removeApp}
visible={modalVisible}
/>
</div>
);
};
export default Management;
The page where I transfer the props.
import React, { useState, useEffect } from "react";
import Accordion from "../components/Accordion";
import Details from '../components/Details'
import {
getByIdApps,
} from "../api/apiCalls";
const ApplicationManagement = (props) => {
const [appById, setAppById] = useState([]);
const { id } = props.location;
const [selectOption, setSelectOption] = useState('add')
useEffect(() => {
getData();
getMode();
}, [])
const getData = async () => {
console.log(props.location.id)
if (props.location.id) {
await getByIdApps(props.location.id).then((response) => setAppById(response.data))
console.log(appById)
console.log(props)
}
else {
setSelectOption('add')
}
}
const getMode = () => props.location.mode ? setSelectOption(props.location.mode) : setSelectOption('add')
const handleOptionChange = (event) => {
console.log(event.target.value)
setSelectOption(event.target.value)
}
return (
<>
<div style={{ margin: 20 }}>
<h1>
{appById.app_shortcode} - {appById.app_fullname}
</h1>
<div className="float-right mb-auto">
<label><input type="radio" value="view" checked={selectOption === 'view'} onChange={handleOptionChange} />View</label>
<label> <input type="radio" value="add" checked={selectOption === 'add'} onChange={handleOptionChange} />Add</label>
<label> <input type="radio" value="edit" checked={selectOption === 'edit'} onChange={handleOptionChange} />Edit</label>
</div>
<br></br>
<div style={{ marginLeft: 50, marginRight: 50 }} >
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Links</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Factory Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Issues Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
<Accordion title={
<div style={{ width: 1350 }}>
<h3>Middleware Management</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}></Accordion>
</div>)
{selectOption === 'add' ? (
<div>
Add Mode
</div>
) : selectOption === 'view' ? (<div>View Mode</div>) : (<div>eidt</div>)}
</div>
</>
);
};
export default ApplicationManagement;
and the section where the details are kept on the ApplicationManagement page (My code is quite long, I just share the problem part.)
import React, { useState, useEffect } from 'react'
import axios from "axios";
import {
getResponsibleTeams,
getBusinessAreas
} from '../api/apiCalls'
const Details = (props) => {
const [rTeams, setrTeams] = useState([]);
const [bAreas, setbAreas] = useState([]);
const { data } = props;
useEffect(() => {
async function fetchData() {
const getrTeams = await getResponsibleTeams();
const getbAreas = await getBusinessAreas();
axios.all([getrTeams, getbAreas]).then(
axios.spread((...allData) => {
const allrTeams = allData[0].data;
const allbAreas = allData[1].data;
setrTeams(allrTeams);
setbAreas(allbAreas);
})
);
}
fetchData();
}, []);
return (
<div>
<div
style={{
float: "left",
width: 1350,
height: 340,
}}
>
<div className="form-group">
<label style={{ float: "left" }} htmlFor="appFullName">
Frontend:{" "}
</label>
<input
id="appFullName"
type="text"
class="form-control"
placeholder="dsfdsdsf"
value={data.frontend.frontend_name} // error here
//onChange={handleInputChange}
name="appShortCode"
style={{ width: 400, marginLeft: 150 }}
/>
</div>
</div>
</div>
)
}
export default Details;
Later I realized that using asynchronous functions caused some problems. I came up with a solution to this and the problem was solved.
Error Code Here :
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>
and the solution to the problem
{appById &&
<Accordion
title={
<div style={{ width: 1350 }}>
<h3>Details</h3>
<hr style={{ backgroundColor: "#aaa" }}></hr>
</div>
}
content={
<Details appID={id} data = {appById}></Details>
}
/>}

React updates state when clicked twice

Having an issue with react not updating the state right away on the console.log, i have to click twice on the submit button in order for the console.log to show the updated state
i checked this, but i don't think that could be the issue
React: state not updating on first click
Working Demo, check the console out
https://codesandbox.io/s/l499j0p5vm?fontsize=14
Here is what i have
App.js
import React, {Component} from 'react';
import Navbar from './components/Navbar';
import {withStyles} from '#material-ui/core/styles';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import logo from './logo.svg';
import {Typography, Button} from '#material-ui/core';
import Footer from './components/Footer';
import Emoji from './components/Emoji';
import TextField from '#material-ui/core/TextField';
import EmojiPicker from 'emoji-picker-react';
import JSEMOJI from 'emoji-js';
import Icon from '#material-ui/core/Icon';
let jsemoji = new JSEMOJI();
// set the style to emojione (default - apple)
jsemoji.img_set = 'emojione';
// set the storage location for all emojis
jsemoji.img_sets.emojione.path = 'https://cdn.jsdelivr.net/emojione/assets/3.0/png/32/';
// some more settings...
jsemoji.supports_css = false;
jsemoji.allow_native = true;
jsemoji.replace_mode = 'unified'
const styles = theme => ({
shadows: ["none"],
spacing: 8,
root: {
flexGrow: 1,
minHeight: '800px',
width: '100%',
position: 'relative'
},
paper: {
padding: theme.spacing.unit * 2,
textAlign: 'left',
width: '500px',
color: theme.palette.text.secondary
},
textField: {
width: '400px'
},
myitem: {
margin: '40px'
},
emoji: {
margin: '40px'
},
emojiButton: {
margin: '20px 0px'
},
cancel: {
margin: '20px 0px'
}
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
emoji: '',
text: '',
items: [],
emojiToggle: false
}
}
onChange = (e) => {
e.preventDefault()
this.setState({text: e.target.value});
}
handleClick = (n, e) => {
let emoji = jsemoji.replace_colons(`:${e.name}:`);
this.setState({
text: this.state.text + emoji,
});
// console.log(this.state.items)
}
handleButton = (e) => {
e.preventDefault();
if(!this.state.emojiToggle){
this.setState({emojiToggle: true})
}
else{
this.setState({emojiToggle: false})
}
}
onSubmit = (e) => {
e.preventDefault();
this.setState({
text: this.state.text,
items: [this.state.text]
})
console.log(this.state.items) // have to click twice to see the updated state
}
render() {
const {classes} = this.props;
return (
<div className={classes.root}>
<Navbar/>
<Grid container spacing={12}>
<Grid item sm={6} className={classes.myitem}>
<Paper className={classes.paper}>
<Typography variant="h2" component="h2">
Insert An Emoji
</Typography>
{/* Begin Form */}
<form>
<TextField
id="standard-name"
label="Enter Something"
className={classes.textField}
value={this.state.text}
onChange={this.onChange}
margin="normal"
/>
{this.state.emojiToggle ? (
<div>
<EmojiPicker onEmojiClick={this.handleClick}/>
<Button
className={classes.cancel}
onClick={this.handleButton}
color="danger"
variant="outlined">
Close
</Button>
</div>
)
: (
<div>
<Button onClick={this.handleButton} color="primary" variant="outlined">
Show Emojis
</Button>
<Button onClick={this.onSubmit} style={{ marginLeft: '10px'}} color="primary" variant="outlined">
Submit
</Button>
</div>
)}
{/* End Form */}
</form>
</Paper>
</Grid>
</Grid>
<Footer/>
</div>
);
}
}
export default withStyles(styles)(App);
Console.log() exec before the state finish to update.
Because setState() is an asynchronous function
this.setState({
text: this.state.text,
items: [this.state.text]
}, () => console.log(this.state.items));
The problem is exactly what is mentioned in the post you linked. setState is an asynchronous function and doesn't necessarily set the state of your component before your console.log() is called. If you would like to see your new state after it is updated, you can add a callback function to setState to see what the results of the state update are.
this.setState({
text: this.state.text + emoji,
}, () => console.log(this.state.items));
EDIT
Here is a link to your demo with the console.log giving the correct result:
https://codesandbox.io/s/949xprn3xy
That is the expected behaviour. console.log() is synchronous, so it runs before setState() is finished. If you really want to see the current state, you should change your submit handler to this:
onSubmit = e => {
e.preventDefault();
this.setState(
{
text: this.state.text,
items: [this.state.text]
},
() => console.log(this.state.items)
);
};
Just use async before the function , use await before setState and your issue will be resolved

Resources