Cannot close an antd modal using reactJS library - reactjs

I need your helps, when I do click those 3 buttons on the red circles, It threw an error that's called '... is not a function' though I think closing a modal needs an useState hook which set boolean to close it. Am I wrong? If I'm not wrong so how to solve it, please help me!~~~
Images show error 1
Images show error 2
Here is my code
import {createContext, useState, useContext, useMemo} from 'react'
import { AuthContext } from './AuthProvider'
import useFirestore from '../hooks/useFirestore'
export const AppContext = createContext()
function AppProvider ({children}) {
const [isAddRoomVisible, setIsAddRoomVisible] = useState(false)
const user = useContext(AuthContext)
const {uid} = user
const roomsCondition = useMemo(() => {
return {
fieldName: 'members',
operator: 'array-contains',
value: uid
}
}, [uid])
const rooms = useFirestore('rooms', roomsCondition)
return (
<AppContext.Provider value={[rooms, isAddRoomVisible, setIsAddRoomVisible]}>
{children}
</AppContext.Provider>
)
}
export default AppProvider
import {Modal, Form, Input} from 'antd'
import { useState, useContext } from 'react'
import { AppContext } from '../../Context/AppProvider'
import { AuthContext } from '../../Context/AuthProvider'
import { addDocument } from '../../firebase/service'
export default function AddRoomModal() {
const [isAddRoomVisible, setIsAddRoomVisible] = useContext(AppContext)
const user = useContext(AuthContext)
const {uid} = user;
const [form] = Form.useForm()
const handleOk = () => {
// console.log({
// formData: form.getFieldsValue()
// })
addDocument('rooms', {...form.getFieldsValue(), members: [uid]})
setIsAddRoomVisible(false)
}
const handleCancel = () => {
setIsAddRoomVisible(false)
}
return (
<div>
<Modal
title="Create room"
visible={isAddRoomVisible}
onOk={handleOk}
okCancel={handleCancel}
>
<Form form={form} layout="vertical">
<Form.Item label="Room's name" name="name">
<Input placeholder="Enter room's name here"/>
</Form.Item>
<Form.Item label="Description" name="description">
<Input.TextArea placeholder="Enter description"/>
</Form.Item>
</Form>
</Modal>
</div>
)
}

shouldn't the first row of AddRoomModal be :
const [room, isAddRoomVisible, setIsAddRoomVisible] = useContext(AppContext)
You are destructuring an array not an object, so you cannot skip any element of the value prop of your context
EDIT :
Ok so i'll try to give you more info.
The first thing I notice is you are using a difficult way to handle your modal using a context. it would be easier to include your component in another that will contain the opening button and the modal open state :
const ModalParent = () => {
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<>
<button onClick={() => setIsModalOpen(true)}>openModal</button>
<Modal
title="Create room"
visible={isModalOpen}
onOk={(e) => {
setIsModalOpen(false);
}}
okCancel={(e) => {
setIsModalOpen(false);
}}
>
Hi my Modal !
</Modal>
</>
);
};
But you can still use the context approach, it allows you to control your modal from anywhere in your app, which can be pretty useful.
When you are using your context provider, you are passing it an array containing three values :
<AppContext.Provider value={[rooms, isAddRoomVisible, setIsAddRoomVisible]}>
When you are using the useContext hook, you are retrieving this array in your component.
const myAppContextValues = useContext(AppContext)
const rooms = myAppContextValues[0]
const isAddRoomVisible = myAppContextValues[1]
const setIsAddRoomVisible= myAppContextValues[2]
In you're case, you are using a syntax to define more quickly variables from an array called destructuring https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment .
But you are skipping an element of the array.
So if we translate your code in vanilla JS you are actually doing :
// Here you are retrieving the element 0 & 1 and putting them in variable isAddRoomVisible, setIsAddRoomVisible
// const [isAddRoomVisible, setIsAddRoomVisible] = useContext(AppContext)
// Under is the vanilla JS example
const myAppContextValues = useContext(AppContext)
const isAddRoomVisible = myAppContextValues[0]
const setIsAddRoomVisible= myAppContextValues[1]
So what you need to do is to also assign the room element, even if you don't use it
const [room, isAddRoomVisible, setIsAddRoomVisible] = useContext(AppContext)
You could also use an object as value of your context and destructuring it with the object syntax :
// notice that here i'm using the shorthand syntax to create an object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#new_notations_in_ecmascript_2015
<AppContext.Provider value={{rooms, isAddRoomVisible, setIsAddRoomVisible}}>
// and here i'm using the object destructuring
const {isAddRoomVisible, setIsAddRoomVisible} = useContext(AppContext)
Hope it helps you and it is clearer !

Related

React Context Not Updating when clicking open modal button

I am trying to open a modal by updating the state of the component. The component is wrapped in a Context Provider.
Although the button seems to be clicking successfully, the Modal will not open
Here is the code with the container which contains the "Open Modal Button"
import { type FC, useRef } from 'react'
import infomation from '#/assets/icons/infomation.svg'
import { useModal } from '#/providers/ModalProvider'
import styles from './Instructions.module.scss'
const Instructions: FC = () => {
const card = useRef<HTMLDivElement>(null)
const { openDemoModal } = useModal()
const onOpenModalClick = () => {
openDemoModal()
console.log(openDemoModal)
}
return (
<section ref={card} className={`card ${styles.card}`}>
<div className={styles.background} />
<div>OPEN THE MODAL DOWN BELOW</div>
<button variant="outlined" fullWidth onClick={onOpenModalClick}>
Open Modal
</button>
</section>
)
}
export default Instructions
Here is the file which contains the Context for the Modal, I have tried setting up the context in INITIAL_STATE and tried updating it using the "onOpenModalClick" function - but it doesn't seem to be able to update the ShowModal.current value below.
import { type FC, type PropsWithChildren, createContext, useContext, useRef } from 'react'
import Modal from '#/components/modal/Modal'
type ContextValue = {
showModal: boolean
openDemoModal: () => void
}
const INITIAL_STATE: ContextValue = {
showModal: false,
openDemoModal: () => {},
}
export const ModalContext = createContext(INITIAL_STATE)
export const ModalProvider: FC<PropsWithChildren> = ({ children }) => {
const showModal = useRef(INITIAL_STATE.showModal)
const openDemoModal = () => {
showModal.current = true
}
console.log(showModal.current)
return (
<ModalContext.Provider value={{ showModal: showModal.current, openDemoModal }}>
{children}
<Modal show={showModal.current} setShow={(shouldShow: boolean) => (showModal.current = shouldShow)} />
</ModalContext.Provider>
)
}
export function useModal() {
const context = useContext(ModalContext)
if (!context) {
throw new Error('useModal must be used within a ModalProvider')
}
return context
}
Is there any way to update the onOpenModalClick button to make it change the value of showModal.current in the Provider file?
Sorry if the post is unclear, this is my first Stack Overflow post. Let me know if I need to post anymore of the components.
I tried to add a button to the component which updates the Context, however the state failed to update
The useRef does not trigger a re-render when the value changes. Instead you can use a useState hook. Which would look something like this.
type ContextValue = {
showModal: boolean;
openDemoModal: () => void;
};
const INITIAL_STATE: ContextValue = {
showModal: false,
openDemoModal: () => console.warn('No ModalProvider'),
};
export const ModalContext = createContext(INITIAL_STATE);
export const ModalProvider: FC<PropsWithChildren> = ({ children }) => {
const [showModal, setShowModal] = useState(false);
const openDemoModal = () => {
setShowModal(true)
};
console.log(showModal);
return (
<ModalContext.Provider
value={{ showModal, openDemoModal }}
>
{children}
<Modal
show={showModal}
setShow={setShowModal}
/>
</ModalContext.Provider>
);
};

How to stop rendering every keystroke input field onChange in React

Is there a better way to stop rendering every keystroke input field onChange in React... I noted that if I changed the value to onBlur() on input field, however it doesn't dispatch AddReservation function the second part to clear the input field (setReservationCardInput('')).
Or I cannot stop rendering onChange due to setReservationCardInput update reservationCardInput with useState() function?
My application is below, appreciate your feedback, thank you!
import React, {useState} from 'react'
import {useSelector, useDispatch} from 'react-redux'
import ReservationCard from '../../components/ReservationCard'
import {addReservation} from '../reservation/reservationsSlice'
const ReservationsList = () => {
const reservations = useSelector(state => state.reservations.value)
const [reservationCardInput, setReservationCardInput] = useState('')
const dispatch = useDispatch()
const inputOnChange = (e) => {
setReservationCardInput(e.target.value)
}
console.log('reservations:', reservationCardInput)
const AddReservation =() => {
if(!reservationCardInput) return
dispatch(addReservation(reservationCardInput))
setReservationCardInput('')
}
return (
<div className="reservation-cards-container">
{
reservations.map((name, index) => {
return (
<ReservationCard name={name} key={index}/>
)
})
}
<div className="reservation-input-container">
<input value={reservationCardInput} onChange={inputOnChange}/>
<button onClick={AddReservation}>Add Customer</button>
</div>
</div>
)
}
export default ReservationsList

useEffect fails on page refresh

I am an infant programmer and I am trying to fetch an api and style the results using React. My page works fine on the initial load and subsequent saves on VScode,but when I actually refresh the page from the browser I get the error thats posted on imageenter image description here:
Here is my code: App.js
```import React, { useEffect, useState } from 'react';
import './App.css';
import Students from './components/Students';
import styled from 'styled-components';
function App() {
const [studentInfo, setStudentInfo] = useState({});
const [searchResult, setSearchResult] = useState({});
const [searchTerm, setSearchTerm] = useState('');
useEffect(() => {
getStudents();
}, []);
useEffect(() => {
getStudents();
console.log('useEffect');
}, [searchTerm]);
const getStudents = async () => {
const url = 'https://api.hatchways.io/assessment/students';
console.log(url);
fetch(url)
.then((res) => res.json())
.then((data) => {
console.log(data);
searchTerm != ''
? setStudentInfo(filterStudents(data.students))
: setStudentInfo(data.students);
});
};
const filterStudents = (studentsArray) => {
return studentsArray.filter((info) => {
return (
info.firstName.toLowerCase().includes(searchTerm) ||
info.lastName.toLowerCase().includes(searchTerm)
);
});
};
console.log(searchTerm);
return (
<div className="App">
<Students
studentInfo={studentInfo}
setSearchTerm={setSearchTerm}
searchTerm={searchTerm}
/>
</div>
);
}
export default App;```
here is my component Students.js:
```import React, { useState } from 'react';
import styled from 'styled-components';
import GradeDetails from './GradeDetails';
const Students = ({ studentInfo, searchTerm, setSearchTerm }) => {
console.log(typeof studentInfo);
console.log(studentInfo[0]);
const [isCollapsed, setIsCollapsed] = useState(false);
const handleDetails = () => {
setIsCollapsed(!isCollapsed);
};
const average = (arr) => {
let sum = 0;
arr.map((num) => {
sum = sum + parseInt(num);
});
return sum / arr.length.toFixed(3);
};
console.log(isCollapsed);
return (
<Container>
<Input
type="text"
value={searchTerm}
placeholder="Search by name"
onChange={(e) => setSearchTerm(e.target.value.toLowerCase())}
/>
{studentInfo?.map((student) => (
<Wrapper key={student.id}>
<ImageContainer>
<Image src={student.pic}></Image>
</ImageContainer>
<ContentContainer>
<Name>
{student.firstName} {student.lastName}{' '}
</Name>
<Email>Email: {student.email}</Email>
<Company>Company: {student.company}</Company>
<Skills>Skill: {student.skill}</Skills>
<Average>Average:{average(student.grades)}%</Average>
</ContentContainer>
<ButtonContainer>
<Button onClick={handleDetails}>+</Button>
</ButtonContainer>
{isCollapsed && <GradeDetails studentInfo={studentInfo} />}
</Wrapper>
))}
</Container>
);
};```
Every time I have the error, I comment out the codes in Students.js starting from studentInfo.map until the and save and then uncomment it and save and everything works fine again.
I am hoping someone can help me make this work every time so that I don't have to sit at the edge of my seat all the time. Thank you and I apologize for the long question.
You are using an empty object as the initial state for studentInfo (the value passed to useState hook will be used as the default value - docs):
const [studentInfo, setStudentInfo] = useState({});
.map is only supported on Arrays. So this is failing when the component is rendering before the useEffect has completed and updated the value of studentInfo from an object, to an array. Try swapping your initial state to be an array instead:
const [studentInfo, setStudentInfo] = useState([]);

SingleDatePicker by react-dates is not closing on change

Hey I have set up a react dates functionality on my app, however the date, when chosen doesnt make the calendar close.
I have the example here on https://codesandbox.io/s/magical-dubinsky-xuxkj?file=/src/App.js:0-681
import React, { useState } from "react";
import { SingleDatePicker } from "react-dates";
import "react-dates/initialize";
import "react-dates/lib/css/_datepicker.css";
const CreateGroupEvent = (props) => {
const [dob, setDob] = useState(null);
const [focused, setFocused] = useState(false);
const setDate = (date) => {
setDob(date);
setFocused(false);
};
return (
<>
<SingleDatePicker
date={dob}
// {...input}
onOutsideClick={true}
numberOfMonths={1}
onDateChange={setDate}
focused={focused}
onFocusChange={setFocused}
id="dob"
/>
</>
);
};
export default CreateGroupEvent;
** EDITED : Here is my example code sandbox.
How about trying this?
According to its document, onFocusChange seems to should take { focused : boolean } objects as parameters.
const onFocusChange = ({ focused }) => {
setFocused(focused);
};
<SingleDatePicker
date={dob}
onOutsideClick={true}
numberOfMonths={1}
onDateChange={setDate}
focused={focused}
onFocusChange={onFocusChange}
id="dob"
/>
This seems to be a version related quirk -- if you just do:
onFocusChange={(focusedInput)=> setFocused(focusedInput.focus)}
it'll work.

React - How can a functional component use a 'this' property

I'm trying to make a page where there are multiple instances of the same functional component inside a parent component. Upon clicking anywhere inside the parent component I want each individual child component to check if the 'event.target' is equal to itself, if it isn't, then I want it to do something.
Maybe I got the wrong idea about this, but is there a way to use a 'this' property inside a function component or a way to mimic it?
edit: some code
Parent component:
import React, { useState } from "react"
import ShopppingListItem from './ShoppingListItem'
function ShopppingList(){
const [list, setList] = useState([{id: 0,text: 'dfdsdf'}, {id: 1,text: 'dfdsdf'},
{id: 2,text: 'dfdsdf'}, {id: 3,text: 'dfdsdf'}]) //shopping list example
const [clickedTarget, setClickedTarget] = useState(null)
const handleClick = (e) => {
setClickedTarget(e.target)
}
return(
<ol
className='shopping-list-container'
onClick={handleClick}
>
{
list.map(item => {
return <ShopppingListItem key={item.id} item={item} clickedTarget={clickedTarget}/>
})
}
</ol>
)
}
export default ShopppingList
Child component:
import React, { useState } from 'react'
function ShoppingListItem(props){
const id = props.item.id
const [text, setText] = useState(props.item.text)
const [editMode, setEditMode] = useState(false)
const clickedTarget = props.clickedTarget
const inputStyle={
width: "15vw",
border:"0px",
font:"400 20px Segoe UI",
paddingLeft:"1px"
}
//////////////////////////////////////
React.useEffect(() => {
if (this != clickedTarget) //the idea behind what im trying to achieve
{ do something }
}, [clickedTarget])
//////////////////////////////////////
const handleTextClick = () => {
setEditMode(!editMode)
}
const handleChange = (e) => {
setText(e.target.value)
}
return(
<div className='shopping-list-item'>
<span style={{paddingRight:"5px"}}>{id}. </span>
{!editMode
? <span onClick={handleTextClick}>{text}</span>
: <input
ref={element}
style={inputStyle}
type="text"
value={text}
placeholder={text}
onChange={handleChange}
/>}
</div>
)
}
export default ShoppingListItem

Resources