How to implement custom dropdown toggle state with react bootstrap dropdown - react-bootstrap

I am trying to implement a custom toggle for a react dropdown that has a chevron up and down on the arrow.
I tried moving the toggle inside the LocaleSwitcher function and use the state from that component, but it causes some type of error where the document gets unbound from the component after updating the state once, the next click it breaks.
I'm not familiar with the forward referencing, the code was mostly copy paste from a google article, so I'm not 100% sure how it's working.
My question is, in the code below - is there a way to detect and update the state to switch the up/down arrow?
{**how to toggle a condition to show/hide based on dropdown state? ** ? upArrow : downArrow}
import styles from "./LocaleSwitcher.module.scss";
import Dropdown from "react-bootstrap/Dropdown";
import React, {useState} from 'react';
// found this code on Google.
const downArrow = (
<span>▲</span>
)
const upArrow = (
<span>▼</span>
)
const CustomToggle = React.forwardRef(({ children, onClick }, ref) => (
<a
href=""
ref={ref}
onClick={(e) => {
e.preventDefault();
onClick(e);
}}
>
{children}
{true ? upArrow : downArrow}
</a>
));
const LocaleSwitcher = (props) => {
const data = [
{label: "Australia (AU$)", currency: 'AUD', locale: 'en_AU', link: 'https://www.rmwilliams.com.au'},
{label: "Canada (CAD$)", currency: 'CAD', locale: 'en_CA', link: 'https://www.rmwilliams.com'},
{label: "Denmark (kr.)", currency: 'Kr.', locale: 'en_AU', link: 'https://www.rmwilliams.com.au'}
];
const [locale, setLocale ] = useState(data[0]);
const [show, setShow] = useState(false);
const onToggle = (args) => {
console.log(JSON.stringify(args, null, 2));
setShow(args);
}
const onSelect = (args) => {
const locale = data.find((item) => item.label === args);
console.log(JSON.stringify(args, null, 2));
console.log('found ' + JSON.stringify(locale));
setLocale(locale);
}
return (
<div className={styles.locale_switcher}>
<Dropdown onToggle={onToggle} onSelect={onSelect}>
<Dropdown.Toggle as={CustomToggle} variant="success" id="dropdown-basic">
{locale.label}
</Dropdown.Toggle>
<Dropdown.Menu>
{data.map((locale) => <Dropdown.Item key={locale.label} eventKey={locale.label}>{locale.label}</Dropdown.Item>)}
</Dropdown.Menu>
</Dropdown>
</div>
);
};
export default LocaleSwitcher;

Related

formik initial checkbox value not disabling submit button

In my form, I have a checkbox for captcha that needs toggled for the form to be valid, however on load, the form still shows as valid. I can check the box to enable the form, and then uncheck it again, and THEN the form shows as properly disabled.
I am using the HOC withFormik implementation. I have tried using setTouched & setFieldTouched on the checkbox, and even setting this.props.touched.captcha = true. I have also tried passing isInitialValid: false to withFormik.
Another thing I tried is passing mapPropsToTouched, but that method doesn't get called.
I am using formik v 1.5.8
How can I make the form initially disabled until I engage the checkbox?
export class LoginForm extends React.Component {
render() {
const {
initialValues,
initialTouched,
validationSchema,
onSubmit,
children
} = this.props;
return (
<div>
<Formik
initialValues={initialValues}
initialTouched={initialTouched}
validationSchema={validationSchema}
onSubmit={onSubmit}
render={(formikProps) => {
return (
<Form>
{React.Children.map(children, (child, index) => {
return (
<div className='Form__row'>
{React.cloneElement(child, { index })}
</div>
);
})}
</Form>
);
}}
/>
</div>
);
}
}
const mapDispatchToProps = {};
export function mapStateToProps(state) {
return {};
}
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withFormik({
// isInitialValid: false,
// mapPropsToTouched: (props) => {
// console.log('🚀 ~ file: LoginForm.jsx ~ line 257 ~ props', props);
// },
mapPropsToValues: (props) => {
return {
username: props.previousUsername ? props.previousUsername : '',
password: '',
rememberMe: true,
captcha: false
};
}
})
)(LoginForm);

Need solution to display an image coming from the backend to the frontend

I need a solution or at least a tip for my problem in which I have been stuck quite a long time. So I want to retrieve an image which is coming from the backend through an http request(2 Images , 2 requests). I have used .map() for the text data to be displayed , but how can I use it for inserting the relevant images?
(And I don't think the image can be stored in an array as I have done below)
The 2 images should be in CardImg component and the other should be sent as a prop to another component.
The images are obtained by ID of the collection. Below is the frontend code.
import {Card , CardImg , CardBody , CardTitle , CardText , Button } from 'reactstrap';
import {useState} from 'react';
import { useEffect } from 'react';
import ItineraryContainer from 'components/ItineraryContainer';
import Backdrop from 'components/ItineraryBackdrop';
import styles from '../assets/css/Itinerary.module.css'
import axios from 'axios';
function Itineraries(){
const [itineraries , setItineraries] = useState([]);
const [itineraryImage , setItineraryImage] = useState([]);
const [itineraryCovImage , setCovImage] = useState ([]);
useEffect(() =>{
axios.get("http://localhost:8070/itineraries/").then((res)=>{
setItineraries(res.data);
console.log(res.data);
itineraries.map((i) =>{
const id = i._id;
axios.get(`http://localhost:8070/itineraries/getImage/${id}`).then((r)=>{
itineraryImage.push(r.data);
}).catch((err) =>{
console.log(err);
})
axios.get(`http://localhost:8070/itineraries/getCovImage/${id}`).then((r) =>{
itineraryCovImage.push(r.data);
}).catch((err) =>{
console.log(err);
})
})
}).catch((err)=>{
console.log(err);
})
} , []);
const [ItineraryisOpen , setItineraryOpen] = useState(false);
function ViewItinerary(){
setItineraryOpen(true);
}
function ItineraryisClosed(){
setItineraryOpen(false);
}
return(
<div>
<div className = {styles.Packages}>
<h3>Our Tours</h3>
<br/><br/>
<div className = {styles.container} >
{itineraries.map((itinerary) =>(
<Card style = {{width: '20rem' , margin : '50px'}}>
<CardImg top src = "img.jpg" alt = "TourImage" />
<CardBody>
<CardTitle>{itinerary.itineraryName}</CardTitle>
<CardText>
{itinerary.itineraryDesc}<br/><br/>
<b>Itinerary Days : {itinerary.itineraryDays} </b>
<b>Itinerary Class : {itinerary.itineraryClass}</b>
<b>Price Per Adult : {itinerary.itineraryPriceAdult} </b> <br/>
<b>Price per Child : {itinerary.itineraryPriceChild} </b>
</CardText>
<Button color = "primary" onClick = {ViewItinerary} >View Itinerary</Button>
<Button color = "info" style = {{float : 'right'}}>Book Tour</Button>
</CardBody>
</Card>
))}
</div>
{ItineraryisOpen ? <ItineraryContainer onCancel = {ItineraryisClosed}/> : null}
{ItineraryisOpen && <Backdrop onCancel = {ItineraryisClosed}/>}
</div>
</div>
);
}
export default Itineraries;
Below is the code for the routes
//Fetch one Itinerary Image
router.route("/getImage/:id").get(async(req,res)=>{
const itinerary = req.params.id;
const itin = await Itinerary.findById(itinerary).then((data)=>{
const image = data.itineraryImage;
const file = `./images/${image}`;
res.download(file);
}).catch((err)=>{
res.status(500).send({status : "Fetching Image unsuccesful!"});
})
})
//Fetch one Itinerary Cover Image
router.route("/getCovImage/:id").get(async(req,res)=>{
const itinerary = req.params.id;
const itin = await Itinerary.findById(itinerary).then((data)=>{
const image = data.itineraryCoverImage;
const file = `./images/${image}`;
res.download(file);
}).catch((err)=>{
res.status(500).send({status : "Fetching Image unsuccesful!"});
})
})
(The backend gives the image as response , checked using postman).
Thank you!
Turns out that I have made things complicated. I directly used the HTTP request for the src attribute and it works! (Removed all the code inside the useEffect except for the "itinerary")
This did the job.
<CardImg top src = {`http://localhost:8070/itineraries/getImage/${itinerary._id}`}

i18next with HttpBackend, Trans looking for key before init finished

I'm loading translations dynamically, with the HttpBackend
However inside my component I use the Trans component, and it is screaming about missing translation key, I also see the init finished after it tried to access the Trans component.
I have a Suspens around my app, why is this happens?
I get in the console:
i18next::translator: missingKey en translation myKey This is text that has a in it.... 
the init happens after.
How can I fix this?
// file: i18n.js
i18n
// load translation using http -> see /public/locales (i.e. https://github.com/i18next/react-i18next/tree/master/example/react/public/locales)
// learn more: https://github.com/i18next/i18next-http-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
// pass the i18n instance to react-i18next.
.use(initReactI18next)
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en',
debug: true,
supportedLngs: ['en', 'bg', 'rs'],
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
backend: {
loadPath: '/static/{{lng}}.translations.json',
},
react: {
wait: true,
useSuspense: true,
},
transSupportBasicHtmlNodes: true,
});
export default i18n;
// app.js
function App() {
return (
<BrowserRouter>
<Apollo>
<Suspense fallback={<Loading />}>
<ThemeProvider theme={theme}>
<Header />
<Routes />
<Footer />
</ThemeProvider>
</Suspense>
</Apollo>
</BrowserRouter>
);
}
problematic component:
//home
const I18N_TEXT_KEY_CONSTANT = 'text_key';
const Home = () => (
<Trans i18nKey={I18N_TEXT_KEY_CONSTANT}>
This is text that has a <br /> in it and also some random spaces.
</Trans>
);
You should pass the t function to Trans component.
//home
import { useTranslation } from 'react-i18next';
const I18N_TEXT_KEY_CONSTANT = 'text_key';
const Home = () => {
const { t } = useTranslation();
return (
<Trans i18nKey={I18N_TEXT_KEY_CONSTANT} t={t}>
This is text that has a <br /> in it and also some random spaces.
</Trans>
);
};
The solution which is sadly not document is:
const {t} = useTranstions()
<Trans i18nKey="someKey" t={t}>
Passing the t into the Trans works perfectly.

React-Redux: send a fetched API data from a component to a dynamic route component using a button inside a table

I'm building an app with react-redux and have 3 main problems. In a volenteer component, I get data of volenteers from the store and then show it in a table component by passing it the data and columns (imported as constants).
The Volenteer component :
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { requestVolunteerData } from '../actions';
import { volenteerColumns as columns } from '../utils/constants';
import '../container/App.css';
import Table from '../components/Table/Table';
import Loading from '../components/Loading/Loading';
import {Link} from 'react-router-dom';
const mapStateToProps = state => {
return {
entities: state.requestEntitiesReducer.entities,
isPending: state.requestEntitiesReducer.isPending,
error: state.requestEntitiesReducer.error
}
}
const mapDispatchToProps = dispatch => {
return {
onRequestEntities: () => dispatch(requestVolunteerData())
}
}
class Volenteer extends Component{
componentDidMount () {
this.props.onRequestEntities();
}
render () {
const { entities, isPending} = this.props;
return isPending ?
<Loading />
:
(
<div className='tc'>
<h1 className='f1'>רשימת מתנדבים</h1>
<Table data={ entities } columns={ columns }/>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Volenteer);
The constants file - contain the volenteerColumns :
import {Link} from 'react-router-dom';
// Types
export const REQUEST_ENTITIES_PENDING = 'REQUEST_ENTITIES_PENDING';
export const REQUEST_ENTITIES_SUCCES = 'REQUEST_ENTITIES_SUCCES';
export const REQUEST_ENTITIES_FAILED = 'REQUEST_ENTITIES_FAILED';
// Columns
export const volenteerColumns = [{
dataField: 'name',
text: 'שם פרטי'
}, {
dataField: 'name',
text: 'שם משפחה'
}, {
dataField: 'phone',
text: 'עיר'
},{
dataField: 'yeshuv',
text: 'כתובת'
},{
dataField: 'phone',
text: 'מספר טלפון'
},{
dataField: 'area',
text: 'איזור פעילות מועדף'
},{
dataField: 'occupation',
text: 'תחום עיסוק'
}, {
dataField: 'alertDonationInArea',
text: 'התראה על תרומה באיזור'
}, {
dataField: 'alertdonationNearBy',
text: 'התראה על תרומה קרובה'
}, {
dataField: 'status',
text: 'סטטוס'
}, {
dataField: 'bDate',
text: 'תאריך יום הולדת'
}, {
dataField: "_id",
text: "פעולות",
formatter: (rowContent, row) => {
return (
<Link to={`/volenteerRoute/${_id}`}>
<button className='btn btn-outline-primary btn lg'>view</button>
</Link>
)
}
}];
and its look like this:
all I want is that when I click on the view button, it will go to a new route: <Route path='/volenteerRoute/:id' component={VolenteerDetails} />
and will show te data of this specific volenteer.
my problems are:
For now I want to import the volenteer columns from external file if it possible (for readability reasons and short code) which is not realy constants... because the button should point to a dynamic route,
so how do I change my route by clicking the view button?
I know I don't write it correctly (how to use the formatter)- what is the right way to pass te volenteer ID by that button that found in an external file?
What is the correct way for passing the state of the volenteer to the <Route path='/volenteerRoute/:id' component={VolenteerDetails} />, is it by a Link like this:
<Link to ={{pathname: "/volenteerRoute/:id",
state: { volenteerDetails:
this.state.volenteerDetails}}} >
or by an action of redux (preferred) by another fetch API call? if so, how do I fetch this data? those are my action:
//volunteer
export const requestVolunteerData = () => getData('http://localhost:8000/api/volunteer');
export const requestOneVolunteerData = () => getData('http://localhost:8000/api/volunteer/:id');
that call the getData function:
import {
REQUEST_ENTITIES_PENDING,
REQUEST_ENTITIES_SUCCES,
REQUEST_ENTITIES_FAILED
} from './constants';
export const getData = (url) => (dispatch) => {
dispatch ( {type: REQUEST_ENTITIES_PENDING} );
fetch(url)
.then ( response => response.json() )
.then( resp => dispatch({type: REQUEST_ENTITIES_SUCCES, payload: resp.data }) )
.catch(error => dispatch({type: REQUEST_ENTITIES_FAILED, payload: error}) )
}
but of course the second action does not work because I dont know how to pass the ID from the view button to that action
How does the VolenteerDetails (component that should show a specific volenteer details) get the data? is it by props?
const VolenteerDetails = ({ props}) => {
console.log(props);
return (
<div>
<h2>volenteer details </h2>
</div>
)
}
sorry for the length of this question and thanks for any help!!
Add this Inside route.js:
<Route exact path='/volenteerRoute' component={Volenteer} />
<Route path='/volenteerRoute/:id' component={VolenteerDetails} />
Table view button:
<Link to={{pathname: `/volenteerRoute/${item.id}`}}>View</Link>
You can get the id of clicked view using react-router useParams().
VolenteerDetails.js:
const VolenteerDetails = ({ props }) => {
let { id } = useParams();
console.log(id);
// with id you can fetch API or call action even read data from store
...
}
For making action for VolenteerDetails:
export const requestOneVolunteerData = (id) => getData(`http://localhost:8000/api/volunteer/${id}`);
Bonus: For the best experience using redux and routing I prefer using connected-react-router
Thanks! it was very helpful !! except from the Table view button, it should be:
{
dataField: "_id",
text: "פעולות",
formatter: (cell) => {
return (
<Link to={`/volenteerRoute/${cell}`}>
<button className='btn btn-outline-primary btn lg'>view</button>
</Link>
)
}
}
because the cell of the dataField: "_id" is the one that holds the id of the volenteer here is a helpful link:
https://allenfang.github.io/react-bootstrap-table/advance.html
but I could not make it without your answer, so thanks again!!
I will explore connected-react-router as you recommended

Redux: How to pass store to form created outside the Provider scope

I have written code, which uses a Modal dialog to display a form.
My react app is rendered at "root"
index.html
<div id="root"></div>
App.js
const store = configureStore();
ReactDOM.render(
<Provider store={store}>
<ExampleBasic/>
</Provider>
, document.getElementById('root'));
ExmpleBasic.js
Please ignore state management in component here. this is just for example.
import React, { PureComponent } from 'react';
import Lorem from 'react-lorem-component';
import Modal from '#atlaskit/modal-dialog';
import Button from '#atlaskit/button';
export default class ExampleBasic extends PureComponent {
state = { isOpen: false }
open = () => this.setState({ isOpen: true })
close = () => this.setState({ isOpen: false })
secondaryAction = ({ target }) => console.log(target.innerText)
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<BasicFormContainer />
</Modal>
)}
</div>
);
}
}
BasicFormContainer.js
const mapStateToProps = state => ({
addDesignation: state.designations.addDesignation,
});
const mapDispatchToProps = dispatch => ({
});
export default connect(mapStateToProps, mapDispatchToProps)(BasicForm);
BasicForm.js
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
class BasicForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit(values) {
console.log(values);
}
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.submit)}>
<Field
name="designationName"
component="input"
placeholder="Name"
label="Enter name"
autoFocus
/>
</form>
);
}
}
export default reduxForm({
form: 'BasicForm',
enableReinitialize: true,
})(BasicForm);
However modal is rendered using portal, outside current DOM.
As modal is rendered outside the scope of redux context, it is not getting the
store. and i am getting an error "Uncaught Error: Field must be inside a component decorated with reduxForm()"
Below is link to same kind of problem, where redux form within portal is not working.
Redux Form Wrapped Inside Custom Portal Component?
in React 16 it is handled by portals, but version before then that you can try something like as follow.
export default class ExampleBasic extends PureComponent {
...
static contextTypes = { store: React.PropTypes.object };
render() {
const { isOpen } = this.state;
const actions = [
{ text: 'Close', onClick: this.close },
{ text: 'Secondary Action', onClick: this.secondaryAction },
];
return (
<div>
<Button onClick={this.open}>Open Modal</Button>
{isOpen && (
<Modal
actions={actions}
onClose={this.close}
heading="Modal Title"
>
<Provider store={this.context.store}>
<BasicFormContainer />
</Provider>
</Modal>
)}
</div>
);
}
}
You need to pass in the values of BasicForm.js to the Redux store and dispatch an action from there itself and not from the BasicFormContainer.js. This way, the Modal remains inside of the scope of your root element and thus there is no need to access the store outside of the Provider.
Then update the Redux store based on the values entered in the form. Once, the store is updated, you can then access it from anywhere in your application such as Modal in your case.
I downgraded to version 2.1.0 to solve the problem.

Resources