How to render react component based on event accurately? - reactjs

When event is fired, I got a warning message. What is the best approach to update component.Help please.
Warning: Can only update a mounted or mounting component. This usually
means you called setState, replaceState, or forceUpdate on an
unmounted component. This is a no-op.
import Table from "./Table";
import Tree1 from "./Tree1";
class ComponentView extends Component {
constructor(props) {
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
this.state = {
viewState: <Table />
};
}
onButtonClick(event) {
event.preventDefault()
const btnValue = event.target.value;
switch (btnValue) {
case 'Table':
this.setState({ viewState: <Table /> });
break;
case 'Tree1':
this.setState({ viewState: <Tree1 /> });
break;
default:
break;
}
}
render() {
return (
<div className="animated fadeIn">
<Row>
<Col xs="12" sm="12" lg="12">
<Card>
<CardHeader>
<Button onClick={this.onButtonClick} color="primary" size="sm" value="Table" >Table</Button>
<Button onClick={this.onButtonClick} color="secondary" size="sm" value="Tree1">Tree1</Button>
</CardHeader>
<CardBody className="pb-0" style={{ height: '500px' }}>
{this.state.viewState}
</CardBody>
</Card>
</Col>
</Row>
</div>
)
}
}

You probably need to distill what your problem is - your code runs without warnings.
window.onload = () => {
console.clear();
const Row = p => <div>{p.children}</div>
const Col = p => <div>{p.children}</div>
const Card = p => <div>{p.children}</div>
const CardHeader = p => <div>{p.children}</div>
const CardBody = p => <div>{p.children}</div>
const Button = p => <button {...p} />
const Table = () => <span>Table</span>;
const Tree1 = () => <span>Tree</span>;
class ComponentView extends React.Component {
constructor(props) {
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
this.state = {
viewState: <Table />
};
}
onButtonClick(event) {
event.preventDefault()
const btnValue = event.target.value;
switch (btnValue) {
case 'Table':
this.setState({ viewState: <Table /> });
break;
case 'Tree1':
this.setState({ viewState: <Tree1 /> });
break;
default:
break;
}
}
render() {
return (
<div className="animated fadeIn">
<Row>
<Col xs="12" sm="12" lg="12">
<Card>
<CardHeader>
<Button onClick={this.onButtonClick} color="primary" size="sm" value="Table" >Table</Button>
<Button onClick={this.onButtonClick} color="secondary" size="sm" value="Tree1">Tree1</Button>
</CardHeader>
<CardBody className="pb-0" style={{ height: '500px' }}>
{this.state.viewState}
</CardBody>
</Card>
</Col>
</Row>
</div>
)
}
}
const d = document.createElement('div')
document.body.appendChild(d)
ReactDOM.render(<ComponentView />, d)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.2.0/umd/react-dom.development.js"></script>

Related

hide buttons from interface

I have a modal, and this modal has two interfaces, the first is “QRReader” and the second is “PatientForm”, and the modal has two buttons, the first is “Approve” and the second is “Cancel”.
And I want to hide the two buttons within the interface of the "QRReader"
How can i solve the problem?
And this file contains the entire modal, knowing that the BasicModal tag is modal
import { Button, Col, Row } from "antd";
import {
useState
} from "react";
import { QrReader } from "react-qr-reader";
import patient from "../../../api/nuclearMedicineApi/services/Patient";
import { decrypt } from "../../../utils/decryption";
import PatientForm from "./form";
import { QrcodeOutlined } from '#ant-design/icons';
import BasicModal from "../modal";
import { FormattedMessage } from "react-intl";
import { notify } from "../notification";
const QRScanner = () => {
const [data, setData] = useState<number>(0);
const [patientInfoData, setPatientInfoData] = useState({})
const [modelVisible, setModelVisible] = useState<any>();
console.log('datadatadata: ', data)
const openNotificationWithIcon = () => {
// onSuccess: (data) => {
notify('success', 'ok', 'approve-message');
// },
};
return (
<>
<QrcodeOutlined
className='cursor-pointer'
style={{ fontSize: '2rem' }}
color={'#fff'}
onClick={(e) => {
e.stopPropagation()
setModelVisible(true)
}}
/>
<BasicModal
header={<>
<h2 className='text-primary'><FormattedMessage id="qr-scanner" /></h2>
</>}
content={
<>
{
data !=0 ?
<PatientForm patientInfoData={patientInfoData} data={data} />
:
<Row>
<Col span={18} offset={3}>
<QrReader
onResult={async (result: any, error) => {
if (!!result) {
const t = result?.text;
const d = decrypt(t);
let zz: any = d.match(/(\d+)/)
Math.floor(zz[0])
setData(zz[0]);
const pationInfo = await patient.patientGet({ Id: Number(zz[0]) })
setPatientInfoData(pationInfo)
}
if (!!error) {
console.info(error);
}
}} // style={{ width: '100%' }}
constraints={{ facingMode: 'user' }}
// style={{ width: '100%' }}
/>
</Col>
</Row>
}
<Row>
<Col span={1} offset={3}>
<Button
type='primary'
className='savebtn'
onClick={() => {
patient.switchToPresent(data || 0)
openNotificationWithIcon()
}}
>
<FormattedMessage id={'approve'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={() => {
setModelVisible(false);
setData(0);
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
</>
}
isOpen={modelVisible}
footer={false}
width='50vw'
handleCancel={() => {
setModelVisible(false);
}}
afterClose={
() => setData(0)
}
/>
</>
)
};
export default QRScanner;
I think you should be able to use a similar condition as you are using to determine if you should render patientForm vs QRReader. You could wrap your buttons with something like this
{ data = 0 && (
<Row>
<Col span={1} offset={3}>
<Button
type='primary'
className='savebtn'
onClick={() => {
patient.switchToPresent(data || 0)
openNotificationWithIcon()
}}
>
<FormattedMessage id={'approve'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={() => {
setModelVisible(false);
setData(0);
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
)
}
You can have the same condition for showing the buttons which you have for QRScanner and PatientForm
{data != 0 ? (
<Row>
<Col span={1} offset={3}>
<Button
type='primary'
className='savebtn'
onClick={() => {
patient.switchToPresent(data || 0)
openNotificationWithIcon()
}}
>
<FormattedMessage id={'approve'} />
</Button>
</Col>
<Col span={8} offset={4}>
<Button
type='default'
className='savebtn'
onClick={() => {
setModelVisible(false);
setData(0);
}}
>
<FormattedMessage id={'cancel'} />
</Button>
</Col>
</Row>
) : </>}

How do you pass data from one child component to another sibling?

My goal is to display different values in the < h1 > of the GoalsDone component depending on which TeamCard is being hovered over. Both of these components are rendered inside of the TopGroups component, in these code snippets I am attempting to pass through the parent TopGroups.
Child component displaying the number of goals done:
const GoalsDone = ({displayGoals}) => {
return (
<GameifyStyle className="">
<Col className="GPM">
<img className="pt-3 mx-auto pl-0" src="../images/bullseye.png" />
<h1 className="pt-1"> {displayGoals}75 Goals</h1>
<p>DONE</p>
</Col>
</GameifyStyle>
)
}
Child Component that updates the score after being hovered over:
It currently has an unhandled runtime error "setDisplayGoals is not a function"
const TeamCard = ({data}, {setDisplayGoals}) => {
return (
<TeamCardStyle>
{!data && (
<Spinner />
)}
{data && data.getGroupScores && (
data.getGroupScores.slice(0, 4).map((group, index) => {
return (
<Row onMouseEnter={() => {setDisplayGoals(group.totalScore)}}>
<Col className="teamCard mt-2 mb-2">
<Row>
<p>{seed[index]}</p>
</Row>
<Row>
<Col className="hideSmall">
<img className="mouseOn" src="../images/group.png" />
<img className="mouseOff" src="../images/groupSelected.png" />
</Col>
</Row>
<p>{group.name}</p>
</Col>
</Row>
)
})
)}
</TeamCardStyle>
)
}
Parent component:
ATTN lines 38, 48
const GET_GROUP_SCORES = gql`
query GET_GROUP_SCORES($quarter: String, $groupId: String) {
getGroupScores(quarter: $quarter, groupId: $groupId) {
name
id
totalScore
goalsDone
milestonesDone
}
}
`;
const TopGroups = () => {
const {loading, error, data } = useQuery(GET_GROUP_SCORES, {variables: { quarter: "Q2 2021" }})
if (data) {
const sortedGroups = data.getGroupScores.sort((a, b) => {
if (a.totalScore > b.totalScore) {
return -1
}
if (a.totalScore < b.totalScore) {
return 1
} else {
return 0
}
})
}
if (error) {
return <p>An error has occured</p>
}
if (loading) {
<Spinner />
}
const [displayGoals, setDisplayGoals] = useState('0');
return (
<Col className="col-12">
<TeamCardStyle>
<Row>
<TeamCard
data={data}
setDisplayGoals={setDisplayGoals}
/>
</Row>
</TeamCardStyle>
<GameifyStyle>
<Row className="cardContainer mt-3 XsWidthAdjust">
<Col className="SideBorder TopGroupsFonts mx-1">
<GoalsDone
displayGoals={displayGoals} />
</Col>
<Col className="SideBorder TopGroupsFonts mx-1">
<PrizesWon />
</Col>
<Col className="SideBorderPH TopGroupsFonts mx-1">
<MilestonesOnTrack />
</Col>
</Row>
</GameifyStyle>
</Col>
)
}
The error "Child Component that updates the score after being hovered over: It currently has an unhandled runtime error "setDisplayGoals is not a function"" happens because you are destructuring the props wrong in your TeamCard component. Instead of doing
const TeamCard = ({data}, {setDisplayGoals}) => {
You should do:
const TeamCard = ({data, setDisplayGoals}) => {

Create search box in the list in react js component

Please tell, how to carry out search in the list of components which code is specified below correctly?
The search should be performed by title or full description of the list item.
Component with list of Item components:
const PathItems = () => {
const dispatch = useDispatch();
const pathDescription = useSelector(state => state.firestore.ordered.pathDescription);
const handleClick = (path) => {
dispatch(selectPath(path));
}
if(pathDescription && pathDescription.length !== 0){
return (
<React.Fragment>
<Row>
<Col className="pl-0 border-right border-dark">
{pathDescription && pathDescription.map(item => (
<PathItem
key={item.id}
item={item}
onInfoChange={ handleClick }
/>
))}
</Col>
<Col>
<FullDecript/>
</Col>
</Row>
</React.Fragment>
)
} else {
return (
<h5 className="text-muted text-center text-middle">Add your first route</h5>
)
}
}
export default compose(firestoreConnect(()=> ['pathDescription']))(PathItems);
Item component code:
const PathItem = ({ item, onInfoChange }) => {
const handleClick = () => {
onInfoChange(item);
}
return (
<React.Fragment>
<Card as="a"
style={{cursor: 'pointer'}}
className={'mb-2'}
onClick={ handleClick }>
<Card.Body>
<Row className="align-items-center">
<Col xs={1}>
</Col>
<Col xs={7}>
<h5>{item.title}</h5>
{item.sDescript}
</Col>
<Col xs={4} className="text-right">
<label>{item.length}600 km</label>
</Col>
</Row>
</Card.Body>
</Card>
</React.Fragment>
);
}
export default PathItem;
General view of the described components
Thanks in advance)
...
const [searchQuery, setQuery] = useState("");
const inputEvent = (event) => {
const data = event.target.value;
console.log(pathDescription);
setQuery(data);
}
const filterItems = pathDescription && pathDescription.filter(item => {
return item.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
item.fDescript.toLowerCase().includes(searchQuery.toLowerCase());
})
...
<Col className="pl-0 border-right border-dark" style={divStyle}>
<InputGroup className="mb-3">
<FormControl
type="text"
placeholder="Type..."
aria-describedby="basic-addon2"
value={ searchQuery }
onChange={ inputEvent }
/>
<InputGroup.Append>
<Button variant="outline-secondary">
<img
alt="Logo"
src="https://cdn1.iconfinder.com/data/icons/app-user-interface-line/64/search_focus_user_interface_app_zoom-256.png"
width="25"
height="25"
className="d-inline-block align-top"/>
</Button>
</InputGroup.Append>
</InputGroup>
{filterItems.sort((a, b) => b.favorite - a.favorite).map(item => (
<PathItem
key={item.id}
item={item}
onInfoChange={ handleClick }
/>
))}
</Col>

How to delete an item on the front end from a list of items in ReactJS

I'm trying to delete an item from a list of items that I get dynamically from a REST API. For some reason, my onDelete function is being called when I press the Search button, instead of individually when I want to delete a specific list item. Not sure why.
class Users extends Component {
constructor(props) {
super(props);
this.state = {
searchValue: '',
users: []
};
}
handleOnChange = event => {
this.setState({ searchValue: event.target.value });
};
handleSearch = () => {
this.makeApiCall(this.state.searchValue);
};
onDelete(e) {
console.log('why is this being called?');
}
makeApiCall = async searchInput => {
let res = await axios(
`https://zuul-stage.whatifops.com/v1/user/phone/${searchInput}`
);
this.setState({ users: res.data });
};
render() {
return (
<div>
<input
name='text'
type='text'
placeholder='Search'
onChange={event => this.handleOnChange(event)}
value={this.state.searchValue}
/>
<button onClick={this.handleSearch}>Search</button>{' '}
{this.state.users ? (
<div>
{this.state.users.map((user, index) => (
<div key={user.id}>
<Row>
<Col lg={2} style={{ maxWidth: '9.7%' }}>
<Button
color='danger'
style={{ paddingTop: 12, paddingBottom: 12 }}
onClick={this.onDelete()}
>
<i className='fa fa-trash'></i> Delete
</Button>
</Col>
<Col lg={10}>
<ListGroup>
<ListGroupItem>
<strong>Email:</strong> {user.email}
<strong>Phone:</strong> {user.phone}
</ListGroupItem>
</ListGroup>
</Col>
</Row>
</div>
))}
</div>
) : (
<p>Try searching for a user</p>
)}
</div>
);
}
}
export default Users;
The onDelete function I was using was
onDelete(e){
let id = e.target.id;
let updatedUsers = this.users.filter(user=>user.id!=id)
this.setState({users:updatedUsers })
}
but I was getting an error about the users being undefined, and it was not being called individually onClick. Not sure what I am doing wrong, I thought this would be a simple thing to build but I'm struggling!
The issue is that the onDelete is being called (will get called automatically unless the following is changed)
change:
{this.onDelete()}
to:
{() => this.onDelete()}
or to (once onDelete is bounded correctly):
{this.onDelete}

Component keeps looping

In my componentDidMount() I am calling this.props.getPost(soloPost._id); in which case will use redux. This is working correctly, because if I console.log it I get my desired result. But componentDidMount() is looping continuously. I have a feeling i'm missing something on the component lifecycle
class PostItem extends Component {
constructor(props) {
super(props);
this.state = {
showCommentField: false
};
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState(prevState => ({
showCommentField: !prevState.showCommentField
}));
}
componentDidMount() {
const { soloPost } = this.props;
this.props.getPost(soloPost._id);
console.log(this.props.comments);
}
render() {
const { soloPost, classes } = this.props;
const postContent = (
<div>
<CommentFeed postId={soloPost._id} comments={soloPost.comments} />
<CommentForm postId={soloPost._id} />
</div>
);
return (
<div className={classes.section}>
<GridContainer justify="center">
<GridItem xs={12} sm={10} md={8}>
<hr />
<Card plain profile className={classes.card}>
<GridContainer>
<GridItem xs={12} sm={2} md={2}>
<CardAvatar plain profile>
<img src={soloPost.avatar} alt="..." />
</CardAvatar>
</GridItem>
<GridItem xs={12} sm={8} md={8}>
<h4 className={classes.cardTitle}>{soloPost.name}</h4>
<p className={classes.description}>{soloPost.text}</p>
</GridItem>
</GridContainer>
</Card>
</GridItem>
</GridContainer>
<GridContainer justify="center">
<Tooltip
id="tooltip-tina"
title="Reply to comment"
placement="top"
classes={{ tooltip: classes.tooltip }}
>
<Button
color="primary"
simple
className={classes.footerButtons}
onClick={this.onClick}
>
<Reply className={classes.footerIcons} /> Reply
</Button>
</Tooltip>
</GridContainer>
{this.state.showCommentField ? (
<GridContainer justify="center">
<GridItem xs={12} sm={5} md={5} lg={5}>
{postContent}
</GridItem>
</GridContainer>
) : (
<div />
)}
</div>
);
}
}
PostItem.defaultProps = {
showActions: true
};
PostItem.propTypes = {
soloPost: PropTypes.object.isRequired,
getPost: PropTypes.func.isRequired,
comments: PropTypes.object.isRequired
};
const mapStateToProps = state => ({ comments: state.post.post });
export default connect(
mapStateToProps,
{ getPost }
)(withStyles(sectionBlogInfoStyle)(PostItem));
EDIT
I just tried this and it looped as well.
shouldComponentUpdate(nextProps, nextState) {
const { soloPost } = this.props;
if (nextProps !== this.props) {
this.props.getPost(soloPost._id);
return true;
}
if (nextState !== this.state) {
return true;
}
}

Resources