How to access function component's state variables in ReactJS? - javascript

I've been developing a website and I'm faced with an issue about accessing state varibles to a component. I've a address page(Addresses.jsx file) which has address modal. I'm also have a component which contains address modal's content (Address.jsx). I would like to access state inside Address.jsx file. How can I do that? If you can help me. I would be very appreciate.
Here is my code blocks;
Address.jsx file;
const Addresses = () => {
const [selectedAddress, setSelectedAddress] = useState();
const [modalType, setModalType] = useState();
const {
elementRef: addressModalRef,
show: showAddressModal,
hide: hideAddressModal,
} = useModal();
return (
<>
<div className="bg-light px-3 px-lg-4 py-4">
<h1 className="h4 mb-4">My Addresses</h1>
<div className="row g-3">
<div className="col-12 col-md-6 col-lg-4">
<AddressButton
onClick={() => {
setSelectedAddress(undefined);
setModalType("NewAddress");
showAddressModal();
}}
/>
</div>
{ADDRESSES.map((address, i) => (
<div className="col-12 col-md-6 col-lg-4" key={i}>
<AddressButton
{...address}
onClick={() => {
setSelectedAddress(address);
setModalType("EditAddress");
showAddressModal();
}}
/>
</div>
))}
</div>
</div>
<AddressModal
address={selectedAddress}
elementRef={addressModalRef}
hide={hideAddressModal}
modalType={modalType}
/>
</>)};
export default Addresses;
Address Modal file;
const AddressModal = ({ address, elementRef, hide, modalType }) => {
const onSaveAddress = () => {
console.log(address);
hide();
};
return (
<Modal elementRef={elementRef} centered size="lg" fullscreen>
<ModalBody>
<AddressForm address={address} /> // I would like to get this component's state in onSaveAddress funtion
</ModalBody>
<ModalFooter>
<button
type="button"
className="btn btn-primary flex-grow-1 px-5"
onClick={onSaveAddress}>
Kaydet
</button>
</ModalFooter>
</Modal>)};
export default AddressModal;

Instead of accessing child components state, which is not possible, do the following
Address Model
<AddressForm
address={address}
onSave={onSaveAddress}
/>
Address Form
const AddressForm = ({ onSave }) => {
const handleFormSubmit = () => {
onSave(formData);
};
return (
<form onSubmit={handleFormSubmit}>
</form>
);
}
Then in onSaveAddress, you will have access to the form data.

Related

Make div with react components as children disappear if I click outside of it

I want to make a form that disappears if I click outside of it.
Form Component:
const CreateTaskPopup = (props) => {
const ref = query(collection(db, "tasks"));
const mutation = useFirestoreCollectionMutation(ref);
useEffect(() => {
const closeTaskPopup = (event) => {
if (event.target.id != "addForm") {
props.setTrigger(false)
}
}
document.addEventListener('click', closeTaskPopup);
return () => document.removeEventListener('click', closeTaskPopup)
}, [])
return (props.trigger) ? (
<>
<div className="bg-darkishGrey my-4 p-1 mx-3 ml-16 cursor-pointer block"
id="addForm">
<div className="flex justify-between">
<div className="flex break-all items-center ">
<Image src={fileIcon} className="w-6 mx-2"/>
<div>
<Formik
initialValues={{
taskName: "",
taskIsDone: false,
parentId: props.parentId ? props.parentId : "",
hasChildren: false,
}}
onSubmit={(values) => {
mutation.mutate(values);
props.setTrigger(false);
}}
>
<Form>
<div className="">
<TextInput
placeholder="Type a name"
name="taskName"
type="text"
/>
</div>
</Form>
</Formik>
</div>
</div>
<div className="flex items-center">
</div>
</div>
</div>
</>
) : null
}
export default CreateTaskPopup
Text Input Component:
import { useField } from "formik";
const TextInput = ({ label, ...props}) => {
const [field, meta] = useField(props);
return (
<div>
<label id="addForm" className="text-lightestGrey text-xl block"
htmlFor={props.id || props.name}>
{label}
</label>
<input id="addForm" className="bg-darkishGrey text-xl text-almostWhite my-2
outline-none w-10/12 rounded-sm p-1 mx-3" {...field} {...props} />
{meta.touched && meta.error ? <div>{meta.error}</div>: null}
</div>
);
};
export default TextInput;
I tried giving an id to the elements inside it but it's not the best solution as it has components from the Formik library to which I can't assign an id. I don't know what would be the best solution for this problem.

if i click button then show this button id in reactJs

i create a onclick function const showContent=(id)=>{} and if i click the perticular button they will show id in const showContent = (id)=>{console.log(id)} like this .....
inside thisCart.jsx i rendering CartStyle.jsx using map method so this delete button inside here.
Cart
function Cart() {
const [data, setData] = useState([])
useEffect(() => {
const data = JSON.parse(localStorage.getItem('Cart'));
data.map(async (v) => {
try {
axios.get(`/cart/${v}`)
.then((res) => {
return setData((preV) => {
return [...preV, res.data]
})
})
.catch(e => { console.log(e) })
} catch (e) {
console.log(e);
}
})
}, [])
const showContent=(id)=>{
console.log('this is id', id)
}
return (
<>
<div className=" cartStyle row g-3">
<div className="left col-md-4">
<button className="btn btn-primary">Proceed to Buy</button>
</div>
</div>
<div className="right col-md-8 flex flex-wrap justify-around">
{
data.map((v, i) => {
return <CartStyle
key={i}
id={i}
title={v.title}
price={v.DiscountPrice}
img={v.image}
delete={showContent(i)}
/>
})
}
</div>
</div>
</>
)
}
export { Profile, Cart };
CartStyle
this is a CartStyle means i'm passing data from Cart.jsx to CarStyle.jsx using props
const CartStyle= (props) => {
return (
<>
<div style={{ width: '22em', height: '10em'}} className=" p-2 row g-3 card container rounded-2xl shadow py-2 my-3">
<button id="delBtn" onClick={props.delete} className="btn btn-primary my-2">Delete</button>
</div>
</div>
</>
)
};
export default CartStyle;
<CartStyle
key={i}
id={i}
title={v.title}
price={v.DiscountPrice}
img={v.image}
delete={(i) => showContent(i)} // or delete={showContent}
/>
// CartStyle
<button id="delBtn" onClick={() => props.delete(props.id)} className="btn btn-primary my-2">Delete</button>
You pass your id on your parent compoenent.Then you get the id in your child component as props.
const CartStyle= (props) => {
return (
<>
<div style={{ width: '22em', height: '10em'}} className=" p-2 row g-3
card container rounded-2xl shadow py-2 my-3">
<button id="delBtn" onClick={() => props.delete(props.id)}
className="btn btn-primary my-2">Delete</button>
</div>
</>
)
};
export default CartStyle
You need to assign a function to delete prop instead of invoking it directly.
Replace
<CartStyle
delete={showContent(i)}
/>
with
<CartStyle
delete={() => showContent(i)}
/>

How to get clicked element datas inside React Bootstrap Modal?

I have a problem with getting the current clicked element's data's from the API and displaying it inside React Bootstrap Modal body. The parent component is TopMovies.js which looks like:
import React, { useState } from 'react';
import TopMoviesModal from '../top-movies-modal/TopMoviesModal';
const TopMovies = ({ currentMovies }) => {
const [isOpen, setIsOpen] = useState(false);
const showModal = () => {
setIsOpen(true);
};
const hideModal = () => {
setIsOpen(false);
};
const renderMovies = () => {
if (Object.keys(currentMovies).length && currentMovies.length) {
return (
<div className="movies-labels-container">
{currentMovies.map((movie) => {
return (
<div className="movie-label"
key={movie.id.attributes['im:id']} title={movie['im:name']['label']}
onClick={showModal}>
<img className="movie-img" src={movie['im:image'][2]['label']} alt={movie['im:name']['label']} />
<br />
<div className="text-under">
<span id="movie-title">{movie['im:name']['label']}</span>
<br />
<span id="movie-subtitle">{movie.category.attributes.term}</span>
</div>
</div>
);
})}
</div>
);
}
};
return (
<section className="top-movies-list">
<div className="container-outter">
<div className="container-fluid">
<div className="row">
<div className="col-lg-12 col-md-12 col-sm-12" id="top-movies-module">
<div className="top-movies-container">
<h1 className="container-title">
TOP 100 movies
</h1>
{renderMovies()}
<TopMoviesModal
currentMovies={currentMovies}
hideModal={hideModal}
isOpen={isOpen} />
</div>
</div>
</div>
</div>
</div>
</section >
)}
export default TopMovies;
and the child component TopMoviesModal.js which looks like this:
import React from 'react';
import Modal from "react-bootstrap/Modal";
import Button from "react-bootstrap/Button";
const TopMoviesModal = ({ currentMovies, hideModal, isOpen }) => {
const renderMoviesDetails = () => {
if (Object.keys(currentMovies).length && currentMovies.length) {
return (
<div>
{currentMovies.map((movie, index) => {
return (
<div className="movie-label" key={movie.id.attributes['im:id']}>
<span id="movie-title">{movie['im:name']['label']}</span>
</div>
);
})}
</div>
);
}
};
return (
<Modal
show={isOpen}
onHide={hideModal}
size="lg"
aria-labelledby="contained-modal-title-vcenter"
centered
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">
Modal heading
</Modal.Title>
</Modal.Header>
<Modal.Body>
{renderMoviesDetails()}
</Modal.Body>
<Modal.Footer>
<Button onClick={hideModal}>Close</Button>
</Modal.Footer>
</Modal>
);}
export default TopMoviesModal;
Could you help me to solve this problem? I would be very grateful, thanks a lot

Writing tests to a component using `react-hook-form` library

I have a component with react-hook-form library. This is the component:
export const SetNewPassword = () => {
// ... code
return (
<div className="section">
<div className="container">
<div className="row">
<div className="col-md-6 col-md-offset-3 text-center" style={{ textAlign: 'left' }}>
<form onSubmit={handleSubmit(onSubmit)}>
<Input
titleTranslationKey="profile.register.password"
placeholderTranslationKey="client.newPassword"
name="password"
type="password"
register={register({
required: true,
validate: {
pattern: value => passwordValid(value, email),
differsFromEmail: value => passwordDiffersFromEmail(value, email),
},
})}
validationError={errors.password}
/>
<Input
titleTranslationKey="profile.register.passwordRetype"
placeholderTranslationKey="client.newPasswordRepeat"
name="passwordRetype"
type="password"
register={register({
required: true,
validate: {
passwordRetype: value => passwordRetypeValid(value, getValues().password),
},
})}
validationError={errors.passwordRetype}
/>
<div ref={captchaRef} className="custom-checkbox" aria-live="assertive">
<div className={hasErrorClassAssigner('form-group', state.captchaError)}>
<div className="input-group">
<ReCaptcha
languageCode={langCode}
onSuccess={onCaptchaSuccess}
onExpired={onCaptchaExpired}
/>
{state.captchaError && (
<small className="help-block">
{t('client.validator.required', {
first: t('client.recaptcha'),
})}
</small>
)}
</div>
</div>
</div>
<StandardButton
className="btn main"
text={<I18nText translationKey={'profile.edit.password.set.submit'} />}
/>
</form>
</div>
</div>
</div>
</div>
);
};
Here's the <Input/> component
export const Input = ({
// ...props
}) => (
<div className={hasErrorClassAssigner('form-group', validationError)} aria-live="assertive">
<I18nText
isA="label"
className={`control-label ${!isLabelVisible && 'hidden'}`}
translationKey={titleTranslationKey}
isRtl={isRtl}
/>
<input
className="form-control"
{...{ name, type }}
placeholder={t(placeholderTranslationKey)}
ref={register}
/>
{validationError && (
<small className="help-block">
{getTranslatedValidationMessage(name, titleTranslationKey, validationError.type)}
</small>
)}
</div>
);
And here are my tests for SetNewPassword
describe('<SetNewPassword/>', () => {
const element = (
<I18nProvider i18n={fakeI18n}>
<MemoryRouter>
<SetNewPassword />
</MemoryRouter>
</I18nProvider>
);
describe('Fetching', () => {
it('should fill <Input/> fields', async () => {
const { getByPlaceholderText, container } = render(element);
// const wrapper = mount(element);
console.log(getByPlaceholderText(/New password/i).value);
await act(async () => {
fireEvent.change(getByPlaceholderText(/New password/i), {
target: { value: 'zaq1#WSX' },
});
fireEvent.submit(container.querySelector('form'));
});
console.log(getByPlaceholderText(/New password/i).value);
console.log(container.querySelector('form'));
expect(Array.from(container.querySelector('.form-control'))[0].innerText).toEqual('zaq1#WSX');
});
// it('should fetch after <StandardButton/> is pressed', () => {});
});
});
What I want to do, is test if the <Input/>'s value is being updated. But I have no idea how to test it. I tried wrapper.find('input').at(0).value, and value() and getValue(), and every other possibility I could come up with. The most irritating part is that when I check it with the browser console with $0.value works. It displays the content of the <input/> field.
How can I test the value here?
Please read this one
https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector
querySelector
returns only one element and you are referring to it as it is an array,
so simply change to this should solve it:
expect(container.querySelector('input.form-control').value).toEqual('email#test.com');
Bare in mind that you have two inputs

Tranfser props to sibling component

Instead of% username%, I would like the current name and name to be displayed in the modal title
user's surname. I can't make this task. I
after solving the problem, give ideas how I can optimize my code.
Code:
class App extends React.Component {
state= {
show: false,
};
showModal = () =>{
this.setState({
show: !this.state.show
});
};
render() {
const users = this.props.data.users;
const userList = users.map(user => <User key={user.id} user={user} onOpen={this.showModal} name={user.name} surname={user.surname}/>)
return (
<div className="container">
{userList}
{this.state.show ? < Modal onClose={this.showModal} show={this.state.show}/> : null}
</div>
)
}
}
class User extends React.Component{
onOpen = () => {
this.props.onOpen && this.props.onOpen();
};
render(){
const {avatar, name, surname, city, country} = this.props.user;
return(
<div className="box">
<img src={avatar} alt="" className="avatar" />
<h3 className="box-title">{`${name} ${surname}`}</h3>
<p className="box-description">{`${city}, ${country}`}</p>
<div className="button-wrap">
<a href="#" className="button" onClick={()=> this.onOpen()} >Report user</a>
</div>
</div>
)
}
}
class Modal extends React.Component {
onClose = () => {
this.props.onClose && this.props.onClose();
};
render() {
if(!this.props.show){
return null;
}
// tak wygląda struktura HTML dla modal boxa
return (
<div className="modal">
<div className="modal-background"></div>
<div className="modal-content">
<div className="box">
<h3 className="modal-title">Report user</h3>
<textarea rows="6"></textarea>
<div className="button-wrap">
<a href="#" className="button button-link" onClick={() => {
this.onClose()}}>Cancel</a>
<a href="#" className="button ml-auto" onClick={()=> alert("ok")}>Report</a>
</div>
</div>
</div>
</div>
)
}
}
ReactDOM.render(<App data={data} />, document.querySelector("#app"))
using state ReportUsr to store the user you want to report changed by function this.ReportUsr in App class then pass function as prop Report to User class to call it OnClick with the value surname for that instance of User component
then Modal component created from App class has CONTENT which is the App.state.ReportUsr
< Modal onClose={this.showModal} show={this.state.show} >{this.state.ReportUsr}</ Modal>
LiveExample
https://h4i1e.csb.app/
Code https://codesandbox.io/s/modern-browser-h4i1e

Categories