how to switch language from an array with react hooks - javascript

i want to change the language of a quote, from French to English or inverse when i click on "lang button", the quote is generate randomly from an array when the page start.
this is my array:
const quoteData = [
{
quoteEn:
"Three things cannot be long hidden: the sun, the moon, and the truth.",
quoteFr:
"Trois choses ne peuvent pas être cachées longtemps : le soleil, la lune et la vérité.",
author: "Buddha"
},
{
quoteEn:
"We make a living by what we get, but we make a life by what we give.",
quoteFr:
"On vit de ce que l’on obtient. On construit sa vie sur ce que l’on donne.",
author: "Winston Churchill"
}
export default quoteData;
and this is what i tried:
import React, { useState } from "react";
import quoteData from "./components/quoteData";
import "./App.css";
import { ReactComponent as Github } from "./icons/github-brands.svg";
function App() {
const getRandomQuotes = () => {
const randNumb = Math.floor(Math.random() * quoteData.length);
return quoteData[randNumb];
};
const [quote, setQuote] = useState(getRandomQuotes());
const [author, setAuthor] = useState(getRandomQuotes().author);
const [lang, setLang] = useState("En");
const [nextBtn, setNextBtn] = useState("Next");
const handleClickLang = () => {
if (lang === "Fr") {
setLang("En");
setNextBtn("Next");
setQuote(quote.quoteFr);
} else {
setLang("Fr");
setNextBtn("Suivant");
setQuote(quote.quoteEn);
}
};
return (
<div id="quote-box">
<div className="c1">
<div className="lang">
<button onClick={handleClickLang} className="changeLang">
{lang}
</button>
</div>
<div className="card flow">
<p id="text">{quote}</p>
<p id="author">{author}</p>
</div>
</div>
<div className="c2">
<Github id="github" />
<button onClick={handleClickRandomQuote} id="new-quote">
{nextBtn}
</button>
</div>
</div>
);
}
export default App;
and i get this error : Error: Objects are not valid as a React child (found: object with keys {quoteEn, quoteFr, author}).

You are storing in your quote state an object (the one you randomly select from your array of quoteData), not the string for the quote. This is not bad, as you also want to show the author of the random quote you randomly selected.
Also, when you do:
const [quote, setQuote] = useState(getRandomQuotes());
const [author, setAuthor] = useState(getRandomQuotes().author);
You have to take into account that the first execution of getRandomQuotes will return a random quote that doesn't have to coincide with the second call to getRandomQuotes to get the author. This could cause that your data (quote and author) are not in sync.
So, it seems that storing the author separately from the quote is not necessary. Taking all this into account, you could do something like:
function App() {
const getRandomQuotes = () => {
const randNumb = Math.floor(Math.random() * quoteData.length);
return quoteData[randNumb];
};
const [quote, setQuote] = useState(getRandomQuotes());
const [lang, setLang] = useState("En");
const [nextBtn, setNextBtn] = useState("Next");
const handleClickLang = () => {
if (lang === "Fr") {
setLang("En");
setNextBtn("Next");
} else {
setLang("Fr");
setNextBtn("Suivant");
}
};
return (
<div id="quote-box">
<div className="c1">
<div className="lang">
<button onClick={handleClickLang} className="changeLang">
{lang}
</button>
</div>
<div className="card flow">
<p id="text">{quote[`quote${lang}`]}</p>
<p id="author">{quote.author}</p>
</div>
</div>
<div className="c2">
<Github id="github" />
<button onClick={handleClickRandomQuote} id="new-quote">
{nextBtn}
</button>
</div>
</div>
);
}

Related

I want to change the object to my prev or next object when i click on prev or next button with react

This my api data
i fetch my data from my api this is api data in json format
[
{
"id": "1",
"heading": "Wellcom to Woodmix",
"disc": "This is our discription and we provide always best and value able product our quality also is better then other",
"img": "https://images.pexels.com/photos/87611/sun-fireball-solar-flare-sunlight-87611.jpeg?auto=compress&cs=tinysrgb&w=600"
},
{
"id": "2",
"heading": "Save Nature",
"disc": "stry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a t",
"img": "https://images.pexels.com/photos/1420440/pexels-photo-1420440.jpeg?auto=compress&cs=tinysrgb&w=600"
},
{
"id": "3",
"heading": "Pakistan",
"disc": "College in Virginia, looked up one of the more obscure assage, and going through the cites",
"img": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcReHTRKk6eSVY0cdIDwD_u94q7vzCMqzyxwbA&usqp=CAU"
}
]`
This is my code
fetch with react axios and chang the reviews state store the api array in reviews using setReviews
import React, { useState, useEffect } from 'react'
import axios from 'axios'
const Review = () => {
const [index, setIndex] = useState(0)
const [reviews, setReviews] = useState([])
useEffect(() => {
axios.get('http://localhost/api/read.php').then((res) => {
setReviews(res.data)
})
}, [])
This is my return
return (
<section id='hero'>
{reviews.map((data) => {
const { id, heading, disc, img } = data
return (
<div
id='heroCarousel'
data-bs-interval='5000'
className='carousel slide carousel-fade'
data-bs-ride='carousel'
>
<ol
className='carousel-indicators'
id='hero-carousel-indicators'
></ol>
<div key={id} className='carousel-inner' role='listbox'>
<div className='carousel-item active'>
<img src={img} alt='' className='w-100 h-100' />
<div className='carousel-container'>
<div className='container'>
<h2 className='animate__animated animate__fadeInDown'>
{heading}
</h2>
<p className='animate__animated animate__fadeInUp'>
{disc}
</p>
<a
href='#about'
className='btn-get-started animate__animated animate__fadeInUp scrollto'
>
Read More
</a>
</div>
</div>
</div>
</div>
<a
className='carousel-control-prev'
href='#heroCarousel'
role='button'
data-bs-slide='prev'
>
<span
className='carousel-control-prev-icon bi bi-chevron-left'
aria-hidden='true'
onClick={prevPerson}
></span>
</a>
<a
className='carousel-control-next'
href='#heroCarousel'
role='button'
data-bs-slide='next'
>
<span
className='carousel-control-next-icon bi bi-chevron-right'
aria-hidden='true'
// onClick={nextPerson}
></span>
</a>
</div>
)
})}
</section>
)
}
export default Review
i want to write to function for prevPerson and nextPerson
if any one make this code a carousel then do it please
This code working for me
Blockquote
React
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const Review = () => {
const [loading, setLoading] = useState(true);
const [reviews, setReviews] = useState([]);
const [index, setIndex] = useState(0);
useEffect(() => {
setLoading(true);
axios
.get('http://localhost/api/read.php')
.then((res) => {
setLoading(false);
setReviews(res.data);
})
.catch((err) => {
console.log(err);
});
}, []);
useEffect(() => {
let slider = setInterval(() => {
setIndex((oldIndex) => {
let index = oldIndex + 1;
if (index > reviews.length - 1) {
index = 0;
}
return index;
});
}, 5000);
return () => {
clearInterval(slider);
};
}, [index]);
const nextPerson = () => {
setIndex((oldIndex) => {
let index = oldIndex + 1;
if (index > reviews.length - 1) {
index = 0;
}
return index;
});
};
const prevPerson = () => {
setIndex((oldIndex) => {
let index = oldIndex - 1;
if (index < 0) {
index = reviews.length - 1;
}
return index;
});
};
if (loading) {
return <h1>Loading...</h1>;
}
return (
<section id='hero'>
<div
key={reviews[index].id}
id='heroCarousel'
data-bs-interval='5000'
className='carousel slide carousel-fade'
data-bs-ride='carousel'
>
<ol className='carousel-indicators' id='hero-carousel-indicators'></ol>
<div className='carousel-inner' role='listbox'>
<div className='carousel-item active'>
<img src={reviews[index].img} alt='' className='w-100 h-100' />
<div className='carousel-container'>
<div className='container'>
<h2 className='animate__animated animate__fadeInDown'>
{reviews[index].heading}
</h2>
<p className='animate__animated animate__fadeInUp'>
{reviews[index].disc}
</p>
<a
href='#about'
className='btn-get-started animate__animated animate__fadeInUp scrollto'
>
Read More
</a>
</div>
</div>
</div>
</div>
<a
className='carousel-control-prev'
href='#heroCarousel'
role='button'
data-bs-slide='prev'
>
<span
className='carousel-control-prev-icon bi bi-chevron-left'
aria-hidden='true'
onClick={prevPerson}
></span>
</a>
<a
className='carousel-control-next'
href='#heroCarousel'
role='button'
data-bs-slide='next'
>
<span
className='carousel-control-next-icon bi bi-chevron-right'
aria-hidden='true'
onClick={nextPerson}
></span>
</a>
</div>
</section>
);
};
export default Review;

axios doesnt fetch on refresh, only on first render

So I've been struggling for a while with retrieving data from APIs and or retrieving local json files. I am using React axios but even with the normal fetch method I am having the same issues. When I fetch the endpoint and save the code, my jsx refreshes and the data appears on the screen but then when I refresh the page, it's no longer there and doesn't appear when I refresh again and again. I have no idea what I am doing wrong. I tried to retrieve the data on the parent and set it as props but still the same problem.
My child component:
import React, { useEffect, useState } from 'react';
import './Card.scss';
import axios from 'axios';
import { ellipsisIcon } from '../../constants/images';
import dataJson from './data.json';
const Card = ({ name, color, icon, currentTime }) => {
const [data, setData] = useState([]);
const [daily, setDaily] = useState([]);
const [weekly, setWeekly] = useState([]);
const [monthly, setMonthly] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios.get('data.json');
setData(result.data);
setData(
data.filter((item) => {
return item.title === name;
}),
);
setDaily(data[0].timeframes.daily);
setWeekly(data[0].timeframes.weekly);
setMonthly(data[0].timeframes.monthly);
};
fetchData();
}, []);
return (
<div className="card" style={{ backgroundColor: `${color}` }}>
<img src={icon} alt={`${name} icon`} />
<div className="card__container bg-blue">
<div className="card__top-container flex">
<p className="text-white ">{name}</p>
<div className="card__top__elipse-container">
<img src={ellipsisIcon} alt="ellipsis" />
</div>
</div>
<div className="card__bottom-container">
{currentTime === 0 && (
<>
<h1 className="fs-900 text-white">{daily.current}hrs</h1>
<div className="card__bottom__prev-container">
<p className="text-accent ">
Yesterday -<span>{daily.previous}hrs</span>
</p>
</div>
</>
)}
{currentTime === 1 && (
<>
<h1 className="fs-900 text-white">{weekly.current}hrs</h1>
<div className="card__bottom__prev-container">
<p className="text-accent ">
Last Week -<span>{weekly.previous}hrs</span>
</p>
</div>
</>
)}
{currentTime === 2 && (
<>
<h1 className="fs-900 text-white">{monthly.current}hrs</h1>
<div className="card__bottom__prev-container">
<p className="text-accent">
Last Month -<span>{monthly.previous}hrs</span>
</p>
</div>
</>
)}
</div>
</div>
</div>
);
};
export default Card;
My App (Parent):
import { useState, useEffect } from 'react';
import Card from './components/Card/Card';
import {
pbImage,
ellipsisIcon,
exerciseIcon,
playIcon,
careIcon,
socialIcon,
studyIcon,
workIcon,
} from './constants/images';
const cards = [
{
name: 'Exercise',
color: 'hsl(var(--clr-exercise))',
icon: exerciseIcon,
},
{
name: 'Play',
color: 'hsl(var(--clr-play))',
icon: playIcon,
},
{
name: 'Self Care',
color: 'hsl(var(--clr-care))',
icon: careIcon,
},
{
name: 'Social',
color: 'hsl(var(--clr-social))',
icon: socialIcon,
},
{
name: 'Study',
color: 'hsl(var(--clr-study))',
icon: studyIcon,
},
{
name: 'Work',
color: 'hsl(var(--clr-work))',
icon: workIcon,
},
];
function App() {
const [selectedTime, setSelectedTime] = useState(2);
return (
<div className="app bg-dark">
<div className="main__container grid">
<div className="side__card-container">
<div className="side__card__top flex">
<div className="side__card__top__pb-container">
<img
src={pbImage}
alt="pb"
className="side__card__top__pb-image pb-image"
/>
</div>
<div className="side__card__top__person-container">
<p className="fs-600 text-accent">Report for</p>
<h2 className="fs-800 text-white">Jeremy Robson</h2>
</div>
</div>
<div className="side__card__bottom">
<div>Daily</div>
<div>Weekly</div>
<div>Monthly</div>
</div>
</div>
{cards.map((card, _index) => (
<Card
key={_index}
name={card.name}
color={card.color}
icon={card.icon}
currentTime={selectedTime}
/>
))}
</div>
</div>
);
}
export default App;
As I mentioned in the comments, states are updated asynchronously. So, you should be careful when you use a state immediately after setting its value. In your case, you shouldn’t use the data state because you are not sure that it has a value. Try changing to this.
useEffect(() => {
const fetchData = async () => {
const result = await axios.get('data.json');
const filteredData = result.data.filter((item) => {
return item.title === name;
})
setData(filteredData);
// make sure result data isn’t empty
setDaily(result.data[0].timeframes.daily);
setWeekly(result.data[0].timeframes.weekly);
setMonthly(result.data[0].timeframes.monthly);
};
fetchData();
}, []); // "[]" makes the useEffect callback only run after the first render

Vue 3 component not updating after call from vue-router

I want to build a todo app with ionic-vue. It currently uses vue 3.
I have this overview (called Lists.vue) where it is possible to click on multiple lists (where tasks should be loaded per list). However, everytime when I click on a list, the same data appears! It is as if the component is being reused but not re rendered/ updated.
I have tried all kinds of solutions. One of them was to apply a watch on the ref that is being changed, however, it does not matter, the end result stays the same. Also tried to give :key to router-link, still does not work.
My Lists.vue
<ion-page>
<ion-content v-if="chunk" class="flex flex-col overflow-auto ion-align-self-center content-wrapper">
<ion-toolbar class="mt-2">
<h1 class="text-4xl pl-5 font-semibold">Lijsten</h1>
</ion-toolbar>
<div v-for="(categoryChunk, index) in chunk.value" :key="index" class="flex flex-wrap w-full flex-row justify-around mt-2">
<div v-for="category in categoryChunk" :key="category.id">
<ion-card class='w-40 sm:w-80'>
<router-link :to="{ name: 'Index', params: {categoryId: category.id} }">
<ion-card-header class="flex">
<ion-icon class="mt-5 text-4xl" color="orange-secondary" :icon="allIcons[category.icon]"></ion-icon>
<div class="m-4">
<p><b>{{ category.title }}</b></p>
<p>Taken: {{ category.tasks }}</p>
</div>
</ion-card-header>
<ion-card-content><div class="line-vert"></div></ion-card-content>
</router-link>
</ion-card>
</div>
</div>
</ion-content>
<div v-else>
<ion-spinner class="centered" color="orange" name="crescent"></ion-spinner>
</div>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button color="orange-secondary" #click="setOpen(true)">
<ion-icon class="text-4xl" color="light" :icon="allIcons.add"></ion-icon>
</ion-fab-button>
</ion-fab>
<ion-modal
:is-open="isOpenRef"
css-class="my-custom-class"
>
<create-list v-on:on-close="setOpen(false)"></create-list>
</ion-modal>
</ion-page>
</template>
<script>
import { defineComponent, computed, ref, watch, onBeforeMount } from "vue";
import {
IonPage,
IonCard,
IonCardHeader,
IonIcon,
IonCardContent,
IonFab,
IonFabButton,
IonContent,
IonToolbar,
IonModal,
IonSpinner,
} from '#ionic/vue'
import * as allIcons from 'ionicons/icons'
import getCollection from "../../composables/getCollection"
import CreateList from './CreateList'
export default defineComponent ({
components: {
IonPage,
IonCard,
IonCardHeader,
IonIcon,
IonCardContent,
IonFab,
IonFabButton,
IonContent,
IonToolbar,
IonModal,
CreateList,
IonSpinner,
},
setup() {
const { loadCollection } = getCollection();
const chunk = ref()
// Zet modal open/dicht
const isOpenRef = ref(false);
const setOpen = (state) => isOpenRef.value = state;
// Laad alle categorieën uit de database
const reload = () => {
loadCollection('categories').then(data => {
chunk.value = computed(() => {
// Zet de items uit de database om in delen van twee.
const array = [];
const size = 2;
for(let i = 0; i < data.length; i += size) {
array.push(data.slice(i, i+size));
}
return array;
})
})
}
onBeforeMount(() => {
reload();
})
watch(isOpenRef, () =>{
reload()
})
return {
allIcons,
chunk,
isOpenRef,
setOpen,
}
}
})
</script>
My list called Index.vue (maybe I should just call it list.vue or something...)
<template>
<ion-page>
<ion-content v-if="category">
<ion-toolbar>
<div class="flex justify-between">
<h1 class="font-light pl-5">{{ category.title }}</h1>
<ion-icon class="text-2xl pr-5" :icon="closeOutline" #click="redirectBack()"></ion-icon>
</div>
</ion-toolbar>
{{ category }}
</ion-content>
<div v-else>
<ion-spinner class="centered" color="orange" name="crescent"></ion-spinner>
</div>
</ion-page>
</template>
<script>
import { defineComponent, ref } from "vue";
import { closeOutline } from 'ionicons/icons'
import {
IonPage,
IonContent,
IonToolbar,
IonIcon,
IonSpinner,
} from '#ionic/vue'
import { useRoute, useRouter } from "vue-router";
import getValue from "#/composables/getValue";
export default defineComponent ({
components: {
IonPage,
IonContent,
IonToolbar,
IonIcon,
IonSpinner
},
setup() {
const router = useRouter()
const route = useRoute()
const { loadValue } = getValue()
const category = ref()
// redirect terug naar lists indien men op kruisje klikt.
const redirectBack = () => {
return router.push({name: 'Lists'})
}
// Ophalen van data van een lijst.
loadValue('categories', route.params.categoryId).then(data => {
category.value = data
})
return {
closeOutline,
redirectBack,
category,
}
}
})
</script>
My composable function:
import {ref } from "#vue/reactivity";
import { todoFirestore } from "../firebase/config";
const getValue = () => {
const error = ref(null);
const loadValue = async (collectionName: string, id : string) => {
try {
let res = await todoFirestore.collection(collectionName).doc(id)
.get();
if (!res.exists) {
throw Error('Lijst bestaat niet.');
}
return { ...res.data(), id: res.id }
}
catch (err) {
error.value = err.message
}
}
return { error , loadValue }
}
export default getValue;
If someone knows any possible solutions, or what I'm possibly doing wrong, please help! All solutions are very appreciated.
PS: Due to circumstances, I am currently not able to reply very fast, but I assure you that I will reply to your answers :)
Found the answer to my problem! I had to use watchEffect on the loadValue method in order to recall the data from the database. It would seem that Vue (after some research on the internet) wants to reuse components instead of rerendering them, which is more efficient.
The route params were being updated but the key of the component was not, however.
The setup function on Index.vue (the list of tasks)
setup() {
const router = useRouter()
const route = useRoute()
const { loadValue } = getValue()
const category = ref()
// redirect terug naar lists indien men op kruisje klikt.
const redirectBack = () => {
return router.push({name: 'Lists'})
}
// Ophalen van data van een lijst.
const getCategory = () => {
loadValue('categories', route.params.categoryId).then(data => {
category.value = data
})
}
watchEffect(() => {
getCategory()
})
return {
closeOutline,
redirectBack,
category,
}

React & Redux: "form submission canceled because the form is not connected"

I am still new in React and Redux. So, I know the existence of redux-form, but Im not intend to use in this project. So, what I am doing is creating a form without using redux-form. This form will grab the data from the reducers and pass it to backend API.
This is my main CreateListing.jsx page.
// #flow
import React from 'react';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { SEOService } from '[services]';
import CreateListingFormPage1 from './CreateListing/CreateListingFormPage1';
import CreateListingFormPage2 from './CreateListing/CreateListingFormPage2';
import CreateListingFormPage3 from './CreateListing/CreateListingFormPage3';
import WhereAmI from './CreateListing/WhereAmI';
import SuccessCreateListing from './CreateListing/SuccessCreateListing';
type Props = {
...props...
};
class CreateListing extends React.Component<Props> {
getPageBySequence(pagenum) {
// depending on whether User is logged in or not, show/hide the Login/Signup form which is Page3
let sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage3 ];
if (this.props.isLoggedIn) {
sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage2 ];
}
return sequence[pagenum-1];
}
getSubmitCreateListing = (e) => {
e.preventDefault();
const propertyType = this.props.listingType;
const propertyName = this.props.suggestedBuildings.selected;
const propertyBuildingType = this.props.propertyBuildingType;
const bedrooms = this.props.bed;
const bathrooms = this.props.bath;
const price = this.props.price;
const builtUp = this.props.builtUp;
const title = this.props.title;
const tenure = this.props.tenure;
const description = this.props.description;
/* IN CASE USER NOT YET LOGGGED IN */
if(this.props.isLoggedIn === false) {
const email = this.props.email;
const password = this.props.password;
this.props.cacheCreateListing({ email, password, propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
}
this.props.cacheCreateListing({ propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
if(CreateListingFormPage1.landedTypes.includes(propertyBuildingType)) {
this.props.geocodingRequired(true);
}
else {
this.props.geocodingRequired(false);
}
this.props.onSubmitCreateListing();
}
onAuthenticateAndCreateListingButton() {
if(this.props.isLoggedIn) {
return(
<div role="presentation">
<div className={`column ${this.props.formCurrentPage === 1 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
<div className={`column ${this.props.formCurrentPage === 2 || this.props.formCurrentPage === 3 ? '':'displayNone'}`}>
<button type="submit" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>
</div>
</div>
)
}
return <div className={`column ${this.props.formCurrentPage < 3 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
}
render() {
if(this.props.isListingCreated){
return <SuccessCreateListing />;
}
else if(this.props.isListingLoading){
return <div className="create-listing-spinner" />
}
const CurrentPage = this.getPageBySequence(this.props.formCurrentPage);
return (
<div className={`CreateListing${this.props.isMobile ? '' : ' is-desktop'}`}>
<Helmet>
<title>{ SEOService.getMetaTitle('Create Property Listing') }</title>
{ SEOService.getCanonicalTag('/blogs') }
</Helmet>
<section className="CreateListing--Main">
<div className="CreateListing--Container">
<div className="CreateListing--WhereAmI">
<WhereAmI page={this.props.formCurrentPage} />
</div>
<div className="CreateListing--Body">
<form className="CreateListing--Form" onSubmit={ this.getSubmitCreateListing }>
<CurrentPage />
<div className='columns'>
<div className='column'/>
{/* CHANGE THIS this.props.formCurrentPage < 3 later */}
<div className={`column ${this.props.formCurrentPage > 1 && this.props.formCurrentPage < 4 ? '':'displayNone'}`}>
<button type="button" className="Button button-back" onClick={this.props.onPrevClick}>
BACK
</button>
</div>
{ this.onAuthenticateAndCreateListingButton() }
<div className='column'/>
</div>
</form>
</div>
</div>
</section>
</div>
);
}
};
const MapStateToProps = (state: State) => ({...});
const MapDispatchToProps = (dispatch: Dispatch) => ({
onLoadingCreateListing: () => dispatch({type: 'CREATE_LISTING_LOADING'}),
onSubmitCreateListing: () => dispatch({type: 'CREATE_LISTING_SUBMIT_FORM'}),})
export default connect(MapStateToProps,MapDispatchToProps)(CreateListing);
So, my <input type="text" /> are all from CreateListingFormPage1, CreateListingFormPage2 and CreateListingFormPage3 and put together in <CurrentPage />. My <form onSubmit={...}></form> is in this CreateListing.jsx page. Im not whether it is allowed to do it like this.
So, when I click submit, I got warning of Form submission canceled because the form is not connected.
My example of <input type="" /> in CreateListingFormPage1 are:
// #flow
import React from 'react';
import { connect } from 'react-redux';
import {Dropdown} from '[elements]';
type Props = {...props...};
class CreateListingFormPage2 extends React.Component<Props> {
static get selectTenure() { return ["Select Tenure"].concat(this.tenureTypes) };
static get selectTitle() { return ["Select Title"].concat(this.titleTypes) };
static get selectBedroom() { return["Select Bedrooms no"].concat(this.bedroomNo) };
static get selectBathroom() { return["Select Bathrooms no"].concat(this.bathroomNo) };
static get tenureTypes(){
return[
"FREEHOLD",
"LEASEHOLD",
"OTHERS"
]};
static get titleTypes(){
return[
"RESIDENTIAL",
"COMMERCIAL",
"INDUSTRIAL"
]};
static get bedroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
static get bathroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
get selectTenure() { return this.constructor.selectTenure; }
get selectTitle() { return this.constructor.selectTitle; }
get selectBedroom() { return this.constructor.selectBedroom; }
get selectBathroom() { return this.constructor.selectBathroom; }
get tenureTypes() { return this.constructor.tenureTypes; }
get titleTypes() { return this.constructor.titleTypes; }
get bedroomNo() { return this.constructor.bedroomNo; }
get bathroomNo() { return this.constructor.bathroomNo; }
hasInputError = (name) => {
if (this.props.errors[name]) {
return ' is-error';
}
return '';
}
render() {
return (
<div className={`Listing--Create${ this.props.isMobile ? '' : ' is-desktop' }`} id='form-second-page'>
{/* <form className="Listing--form"> */}
<div className="Listing--bedrooms-bathrooms">
<div className="type-title">No. of Bedrooms</div>
<Dropdown namespace="bedroom" selected={ this.selectBedroom[0] } options={ this.selectBedroom } onOptionSelect={ this.onBedroomDropdownSelect }/>
<div className="type-title">Asking Price</div>
<input className={`text-input price-input${ this.hasInputError('price')}`} type="text" onChange={ (e) => this.props.onPrice(e.currentTarget.value) } value={this.props.price} placeholder="RM"/>
</div>
<div className="Listing--price-built-up">
<div className="type-title">No. of Bathrooms</div>
<Dropdown namespace="bathroom" selected={ this.selectBathroom[0] } options={ this.selectBathroom } onOptionSelect={ this.onBathroomDropdownSelect }/>
<div className="type-title">Built-up Size</div>
<input className={`text-input built-up-input${ this.hasInputError('built_up_size')}`} type="text" onChange={ (e) => this.props.onBuiltUpSize(e.currentTarget.value) } value={this.props.builtUp} placeholder="sqft."/>
</div>
<div className="Listing--tenure">
<div className="type-tenure">Select Tenure</div>
<Dropdown namespace="tenure" selected={ this.selectTenure[0] } options={ this.selectTenure } onOptionSelect={ this.onTenureDropdownSelect }/>
</div>
<div className="Listing--title">
<div className="type-title">Select Title</div>
<Dropdown namespace="title" selected={ this.selectTitle[0] } options={ this.selectTitle } onOptionSelect={ this.onTitleDropdownSelect }/>
</div>
<div className="Listing--photos">
<div className="type-title">Upload Photos</div>
<button className={`text-input photos-input${ this.hasInputError('photos')}`}>Click to upload</button>
</div>
<div className="Listing--description">
<div className="type-title">Describe your property</div>
<textarea className={`text-input description-input${ this.hasInputError('description')}`} onChange={ (e) => this.props.onDescription(e.currentTarget.value) } value={this.props.description} placeholder="Describe your property"/>
</div>
</div>
);
}
};
const MapStateToProps = (state: State) => ({
...
});
const MapDispatchToProps = (dispatch: Dispatch) => ({
...
})
export default connect(MapStateToProps, MapDispatchToProps)(CreateListingFormPage2);
Basically, there is nothing wrong with my redux store. All the value of the input is captured successfully. The problem is when submitting the form, either the onSubmit or my form structure method is incorrect.
This is CreateListing.js reducer should it be helpful:
const INITIAL_STATE= {
isListingLoading: false,
isListingCreated: false,
}
const CreateListing = (state = INITIAL_STATE, action) => {
switch(action.type){
case 'CREATE_LISTING_LOADING':
return Object.assign({}, state, {isListingLoading: true});
case 'CREATE_LISTING_SUBMIT_FORM':
return Object.assign({}, state, {isListingCreated: true});
default:
return state;
} }
export default CreateListing;
Any help is much appreciated.
if you have any other buttons in your form you should add type="button".
so make changes like this.
<button type="button" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>

Search functionality in reactjs

import React, { PropTypes } from 'react';
import { Link, browserHistory } from 'react-router';
import * as DataConnectionAction from '../../actions/dataconnectionAction.jsx';
import DataConnectionStore from '../../store/dataconnectionstore.jsx';
class DataSource extends React.Component {
constructor(props) {
super(props);
this.state = {
datasourcelist: [],
};
this._dataconnectionStoreChange = this._dataconnectionStoreChange.bind(this);
}
componentWillMount() {
DataConnectionStore.on('change', this._dataconnectionStoreChange);
}
componentWillUnmount() {
DataConnectionStore.removeListener('change', this._dataconnectionStoreChange);
}
componentDidMount() {
DataConnectionAction._getDataSourcesList();
}
_dataconnectionStoreChange(type) {
if (type == 'DataSourcesList') {
let datasourcelist = DataConnectionStore._getDataSourceList() || {};
this.setState({ datasourcelist: datasourcelist.dataconnections });
}
}
DataSourceView(el) {
let data = {
id: el.dataConnectionName
}
}
_handleSearchChange(e) {
let value = e.target.value;
let lowercasedValue = value.toLowerCase();
let datasourcedata = this.state.datasourcelist;
let datasourcelist = datasourcedata && datasourcedata.filter(el => el.dataConnectionName.toLowerCase().includes(lowercasedValue));
this.setState({ datasourcelist });
}
DataSourcesCardUI() {
let datasourcedata = this.state.datasourcelist;
return (
datasourcedata && datasourcedata.map((el) =>
<div key={el.key}>
<div className="col-md-3 topadjust">
<div className="panel panel-default datasource_panel ">
<div className="panel-heading">
<h5 className="panel_title"><i className="fa fa-database"></i> {el.dataConnectionName}</h5>
</div>
<Link className="panel-body" onClick={this.DataSourceView.bind(this, el)}>
<div className="datasource_txt text-center">
<h6>{el.databaseHost}</h6>
<h6>{el.dataConnectionType} </h6>
<p>{el.createdDate}</p>
</div>
</Link>
</div>
</div>
</div>
)
);
}
render() {
return (
<div>
<section className="content_block">
<div className="container-fluid">
<div className="row dashboard_list">
{this.DataSourcesCardUI()}
</div>
</div>
</section>
</div>
);
}
}
export default DataSource;
Here I am getting one issue, that is I can able to filter based on the dataConnectionName, but when I am trying to filter with change of name it is filtering from the first filter array data.
But, I need to filter based on data array if i remove and type again.
Example:
when I tried search with Cu I am getting properly. but again when i remove Cu and search for User It is not searching from data array It is searching from filter array data. Instead of that when i remove and search with other key it should get filtered from data array.
Please Guide me what i am doing wrong.
Instead of overwriting the data in your state, you could keep a separate array in which you put all the elements that match the search.
Example
let data = [
{
dataConnectionName: "Customer_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-23",
createBy: "Admin"
},
{
dataConnectionName: "User_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-24",
createBy: "Admin"
},
{
dataConnectionName: "Manager_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-25",
createBy: "Admin"
},
{
dataConnectionName: "Director_Details",
dataConnectionType: "NO_SQL",
databaseHost: "17.8.10.26",
pluginName: "AGT1_Customer_Details",
createdDate: "2018-09-26",
createBy: "Admin"
}
];
// Give each element a unique id that is used as key
data.forEach(el => el.id = Math.random());
class App extends React.Component {
state = {
data,
filteredData: data
};
_handleSearchChange = e => {
const { value } = e.target;
const lowercasedValue = value.toLowerCase();
this.setState(prevState => {
const filteredData = prevState.data.filter(el =>
el.dataConnectionName.toLowerCase().includes(lowercasedValue)
);
return { filteredData };
});
};
render() {
const { filteredData } = this.state;
return (
<div>
<input onChange={this._handleSearchChange} placeholder="Search"/>
{filteredData.map(el => (
<div key={el.key}>
<div>
{el.dataConnectionName} - {el.pluginName} - {el.createdDate} - {el.createBy}
</div>
</div>
))}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Categories