Not able to set initial values of formik form? - reactjs

I have created a formik form that gets its initial values from the backend database through API calls. Got below error:
Type 'undefined' is not assignable to type 'FormikValues'. TS2322
Not able to resolve the error.
code:
// #ts-ignore
import React, { useState, useEffect } from 'react';
import { Formik, Form, Field } from 'formik';
import {
Typography,
Button,
Grid,
CircularProgress,
Divider,
} from '#material-ui/core';
import * as Yup from 'yup';
import { MyInput } from './comman/MyInput';
import axios from 'axios'
import ThumbUpAltIcon from '#material-ui/icons/ThumbUpAlt';
import Unsubscribed from './Unsubscribed';
const contactSchema = Yup.object().shape({
});
export default function SurveyUnsubscribed(props: any) {
const [isSubmitted, setIsSubmitted] = useState(false);
//const [color, setColor] = useState(true);
const [loading, setLoading] = useState(false);
const [count, setCount] = useState(0);
const [countone, setCountOne] = useState(0);
const [counttwo, setCountTwo] = useState(0);
const [countthree, setCountThree] = useState(0);
const [initialValues, setInitialValues] = useState();
const [state, setState] = useState({
msg: '',
});
let initial:any;
async function getInitialValues() {
try{
const response = await axios.get(`${process.env.REACT_APP_LOCALHOST_DEVELOPMENT_VOTING_API_GET}`)
return response.data;
}catch(error){
console.error(error)
}
}
useEffect(() => {
getInitialValues().then((res:any) => setInitialValues(res));
}, []);
const handleClickCount = () => {
setCount(count + 1);
// setColor(false);
};
const handleClickCountTwo = () => {
setCountTwo(counttwo + 1);
// setColor(false);
}; const handleClickCountThree = () => {
setCountThree(countthree + 1);
// setColor(false);
}; const handleClickCountOne = () => {
setCountOne(countone + 1);
// setColor(false);
};
return (
<React.Fragment>
<Formik
enableReinitialize
initialValues={initialValues}
// count: initial,
// countone: countone,
// counttwo: counttwo,
// countthree: countthree,
// helptext: '',
validationSchema={contactSchema}
onSubmit={(values, { resetForm }) => {
setLoading(true);
console.log(initial)
const data = {
count: values.count,
countone: values.countone,
counttwo: values.counttwo,
countthree: values.countthree,
helptext: values.helptext,
};
console.log(data);
const request = new Request(
`${process.env.REACT_APP_LOCALHOST_DEVELOPMENT_VOTING_API}`,
{
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json',
}),
body: JSON.stringify(data),
},
);
fetch(request)
.then((res) => res.json())
.then((data) => {
if (data.message === 'Thank you for your feedback!') {
setState({
msg: data.message,
});
setIsSubmitted(true);
setTimeout(() => {
setLoading(false);
}, 1500);
} else {
console.log('error');
}
});
setTimeout(() => {
setIsSubmitted(false);
}, 1500);
resetForm();
}}
>
{({ setFieldValue }) => (
<Grid container>
<Grid
item
xs={12}
style={{ paddingLeft: '2em', paddingRight: '2em' }}
>
<Typography
variant="h6"
style={{
marginTop: '1em',
color: '#2F4858',
fontWeight: 'bold',
}}
>
Voting survey </Typography>
<br />
{isSubmitted}
<Form>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="count" component={ThumbUpAltIcon} onClick={handleClickCount} style={{ color: '#C4C4C4', marginRight: '0.4em' }}/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{count}
</Typography>
A </Typography>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="countone" component={ThumbUpAltIcon} onClick={handleClickCountOne} style={{ color: '#C4C4C4', marginRight: '0.4em' }}/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{countone}
</Typography>
B </Typography>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="counttwo" component={ThumbUpAltIcon} onClick={handleClickCountTwo} style={{ color: '#C4C4C4', marginRight: '0.4em' }}/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{counttwo}
</Typography>
C </Typography>
<Typography
variant="body1"
style={{
display: 'flex',
alignItems: 'center',
marginBottom: '1em',
}}
>
<Field type="hidden" name="countthree" component={ThumbUpAltIcon} onClick={handleClickCountThree} style={{ color: '#C4C4C4', marginRight: '0.4em' }}
/>
<Typography
variant="caption"
style={{
position: 'relative',
top: '1.5em',
right: '1.5em',
}}
>
{countthree}
</Typography>
D
</Typography>
<br />
<Typography
variant="h6"
style={{
marginLeft: '2em',
marginTop: '0.5em',
}}
>
What information would help you ?
</Typography>
<Field
id="outlined-multiline-flexible"
type="text"
name="helptext"
component={MyInput}
disabled={isSubmitted}
style={{ marginLeft: '2em' }}
/>
<br />
<br />
<Button
type="submit"
variant="contained"
style={{
background: '#2F4858',
color: 'white',
fontFamily: 'roboto',
fontSize: '1rem',
marginLeft: '2em',
marginBottom: '1em',
}}
>
{loading && (
<CircularProgress
size={25}
color="inherit"
style={{ marginRight: '5px' }}
/>
)}
{loading && <span>submitting</span>}
{!loading && <span>Submit</span>}
</Button>
<br />
{state.msg && (
<Typography
variant="h6"
style={{
color: '#4BB543',
fontFamily: 'roboto-medium',
marginTop: '1em',
}}
>
{' '}
{state.msg}{' '}
</Typography>
)}
</Form>
<Divider
style={{
border: '1px solid #97A1A8',
marginTop: '1em',
marginBottom: '2em',
}}
/>
<Unsubscribed />
</Grid>
</Grid>
)}
</Formik>
</React.Fragment>
);
}
please guys help me with setting formik initial values from the database(Postgresql)

Because you create state initialValues with default value is undefined. So just update default value to an empty object
const [initialValues, setInitialValues] = useState({});

Related

Defining MongoDB Image Array in Next JS

in the following code, how do I define itemData as post.images[i]?
import clientPromise from "../lib/mongodb";
import Box from '#mui/material/Box';
import Avatar from '#mui/material/Avatar';
import IconButton from '#mui/material/IconButton';
import ChatBubbleOutlineRoundedIcon from '#mui/icons-material/ChatBubbleOutlineRounded';
import FavoriteBorderRoundedIcon from '#mui/icons-material/FavoriteBorderRounded';
import ShareRoundedIcon from '#mui/icons-material/ShareRounded';
import Button from '#mui/material/Button';
import ImageList from '#mui/material/ImageList';
import ImageListItem from '#mui/material/ImageListItem';
export default function Posts({ posts }) {
return (
<div>
<ul>
<div style={{ width: '100%' }}>
<Box sx={{ display: 'flex', justifyContent: 'center'}}>
<div style={{ width: '75%' }}>
{posts.map((post) => (
<li style={{ border: '1px solid grey', borderRadius: '12px', margin: "10px", padding: "10px" }}>
<Box sx={{ display: 'flex', justifyContent: 'start', alignItems: 'center'}}>
<Avatar alt={post.userName} src={post.userAvatar}/>
<h4 style={{ paddingLeft:'20px', paddingRight: '20px' }}>{post.userName}</h4>
<p style={{ color: 'grey', fontSize: '12px' }}>{post.timestamp}</p>
</Box>
<Box>
<p style={{ display: 'block', width: '100%', margin: '0'}}>{post.text}</p>
</Box>
<Box sx={{ my: 1, display: 'flex', justifyContent: 'end', alignItems: 'center'}}>
<IconButton><ChatBubbleOutlineRoundedIcon/></IconButton>
<IconButton><FavoriteBorderRoundedIcon/></IconButton>
<IconButton><ShareRoundedIcon/></IconButton>
</Box>
<Box><Button href={post.link} variant="outlined">Link</Button></Box>
<Box>
<ImageList sx={{ width: 500, height: 450 }} cols={3} rowHeight={164}>
{itemData.map((item) => (
<ImageListItem key={item.img}>
<img
src={`${item.img}?w=164&h=164&fit=crop&auto=format`}
srcSet={`${item.img}?w=164&h=164&fit=crop&auto=format&dpr=2 2x`}
alt={item.title}
loading="lazy"
/>
</ImageListItem>
))}
</ImageList>
</Box>
</li>
))}
</div>
</Box>
</div>
</ul>
</div>
)
}
const itemData = [
{
img: 'https://images.unsplash.com/photo-1523217582562-09d0def993a6',
title: 'House',
},
{
img: 'https://images.unsplash.com/photo-1615874694520-474822394e73',
title: 'Living Room',
},
{
img: 'https://images.unsplash.com/photo-1616594039964-ae9021a400a0',
title: 'Bedroom',
},
{
img: 'https://images.unsplash.com/photo-1616486886892-ff366aa67ba4',
title: 'Dining Room',
},
{
img: 'https://images.unsplash.com/photo-1521783593447-5702b9bfd267',
title: 'Bathroom',
}
];
export async function getServerSideProps() {
try {
const client = await clientPromise;
const db = client.db("abode-app");
const posts = await db
.collection("posts")
.find({})
.sort({ metacritic: -1 })
.limit(20)
.toArray();
return {
props: { posts: JSON.parse(JSON.stringify(posts)) },
};
} catch (e) {
console.error(e);
}
}
I have tried loading the images one by one and they work, but prefer to loop over them.

How to set data in modal in nextjs

I'm having trouble getting the data and displaying it on the modal interface when editing, usually I will use useState to update the data but is there any way I can pass the data through the variable?.thanks for your help !
view
Here is my code:
this is my index,
I'm trying to get the name and tags back and show the edit form
this is my index
import type { NextPage } from 'next'
import { useEffect, useState } from 'react'
import {
Box,
Button,
Grid,
Icon,
IconButton,
Menu,
MenuItem,
Typography,
} from '#mui/material'
import { MainLayout } from 'components/Layout/MainLayout'
import { HideAppBar } from '../AppBar/HideAppBar'
import { TagProps } from './components/Tag'
import { TraitProps } from './components/Trait'
import { TraitsPickerModal } from './components/TraitsPickerModal'
import { CreateTagCategoryModal } from './components/CreateTagCategoryModal'
import { ALL_TAG, TagCategoryList } from './components/AllTag'
import { Trait } from './components/Trait'
import EditOutlinedIcon from '#mui/icons-material/EditOutlined'
import DeleteOutlinedIcon from '#mui/icons-material/DeleteOutlined'
import { useStaticTagsViewModelSWR } from './hooks/useStaticArtViewModelSWR'
import { TagCategoryEditModal } from './components/Modal/TagCategoryEditModal'
import { TagCategoryDeleteModal } from './components/Modal/TagCategoryDeleteModal'
import { BlackButton } from 'components/Elements/Button/BlackButton'
import { MaterialIconFont } from 'assets/Styles/GlobalStyles'
import {
useStaticSWRTagsListViewModel,
StaticSWRTagstListKey,
} from './hooks/useStaticSWRTagsListViewModel'
import { mutate } from 'swr'
import { TagCategoryListData } from './interface'
const CONTENT_PADDING = '16px'
const dummyTagCategoryList = [
{
name: 'testes',
tagList: [
{
name: 'niceTag',
traitList: [
{
category: 'Clothes',
name: 'Polka',
icon: '/images/test/body4.png',
},
{
category: 'Clothes',
name: 'Dress',
icon: '/images/test/body5.png',
},
],
},
{ name: 'badTag', traitList: [] },
],
},
{
name: 'cat2',
tagList: [
{ name: 'tag1', traitList: [] },
{ name: 'tag2', traitList: [] },
],
},
]
type TaggedListType = TraitProps[][][]
type UntaggedListType = Record<string, TraitProps[]>[]
type TagCategoryListKey = keyof TagCategoryList
const Tags: NextPage = () => {
/**
* state
*/
const [tagCategoryList, setTagCategoryList] = useState<TagProps[]>([])
const [createModalOpen, setCreateModalOpen] = useState(false)
const [traitsPickerOpen, setTraitsPickerOpen] = useState(false)
const [categoryIndex, setCategoryIndex] = useState(0)
const [tagIndex, setTagIndex] = useState(0)
const [taggedList, setTaggedList] = useState<TaggedListType>([])
const [unTaggedList, setUntaggedList] = useState<UntaggedListType>([{}])
const [isVisibleUntaggedList, setIsVisibleUntaggedList] = useState<boolean[]>(
[...new Array(tagCategoryList.length)].map((n) => Boolean(n))
)
const [categoryAnchorElList, setCategoryAnchorElList] = useState(
[...new Array(tagCategoryList.length)].map(() => null)
)
const [categoryAnchorEl, setCategoryAnchorEl] = useState<null | HTMLElement>(
null
)
const [tagCategoryListData, setTagCategoryListData] = useState<
TagCategoryListData[]
>([])
const categoryMenuOpen = Boolean(categoryAnchorEl)
const [idItemTag, setIdItemTag] = useState(0)
/**
* Hook
*/
const { viewModel, setViewModel } = useStaticTagsViewModelSWR()
const { viewModelTagCategory, setViewModelTagCategory } =
useStaticSWRTagsListViewModel()
/**
* useEffect
*/
useEffect(() => {
setTagCategoryList(dummyTagCategoryList)
}, [])
useEffect(() => {
mutate(StaticSWRTagstListKey)
}, [])
useEffect(() => {
setTagCategoryListData(viewModelTagCategory)
}, [viewModelTagCategory])
useEffect(() => {
setTaggedList((list) =>
Object.assign(
tagCategoryList.map((category) => {
return category.tagList.reduce((memo, tag) => {
return tag.traitList
? [...memo, tag.traitList.map((trait) => trait)]
: memo
}, [])
}),
list
)
)
setIsVisibleUntaggedList((list) => [...list, false])
}, [tagCategoryList])
useEffect(() => {
const categoryUsedTraitList = taggedList.reduce((memo, list) => {
return [...memo, list.flat(2)]
}, [])
setUntaggedList(
categoryUsedTraitList.map((list) => {
return Object.keys(ALL_TAG).reduce((memo, key: TagCategoryListKey) => {
memo[key] = ALL_TAG[key].filter((trait) => {
return list.every((item) => {
return item.name !== trait.name
})
})
return memo
}, {} as TagCategoryList)
})
)
}, [taggedList])
/**
* Handler
*/
const onClickTrait = (pickedList: TraitProps[]) => {
setTaggedList((list) => {
list[categoryIndex][tagIndex] = pickedList
return JSON.parse(JSON.stringify(list))
})
}
const onClickToggleUntaggedComponentsBtn = (index: number) => {
setIsVisibleUntaggedList((list) => {
list[index] = !list[index]
return [...list]
})
}
const addTagCategory = (tagCategory: TagCategoryListData) => {
setViewModelTagCategory([...viewModelTagCategory, tagCategory])
}
const menuClick = (
event: React.MouseEvent<HTMLElement>,
idItemTag: number
) => {
setIdItemTag(idItemTag)
setCategoryAnchorEl(event.currentTarget)
}
const menuClose = () => {
setCategoryAnchorEl(null)
}
const openTagCategoryEditModal = (idItemTag: number) => {
const item = viewModelTagCategory.find((tagId) => tagId.id === idItemTag)
console.log(item?.name)
const itemTags = item?.tagList.map((tag) => {
return {
id: tag.id,
name: tag.name,
}
})
console.log(itemTags)
setViewModel({ ...viewModel, tagCategoryEditModal: { isOpen: true } })
setCategoryAnchorEl(null)
}
const openTagCategoryDeleteModal = (idItemTag: number) => {
console.log(idItemTag)
setViewModel({ ...viewModel, tagCategoryDeleteModal: { isOpen: true } })
setCategoryAnchorEl(null)
}
return (
<MainLayout>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<HideAppBar tabNum={2} />
<MaterialIconFont />
<TagCategoryEditModal />
<TagCategoryDeleteModal />
<Grid
container
sx={{
position: 'relative',
display: 'block',
textAlign: 'left',
padding: CONTENT_PADDING,
}}
>
<Typography
variant="h5"
style={{
textAlign: 'left',
fontWeight: 'bold',
}}
>
Tags
</Typography>
<Box sx={{ display: 'flex' }}></Box>
<Typography
variant="h6"
style={{ color: '#888888', textAlign: 'left' }}
>
Use tags to organize and group your token traits.
</Typography>
<BlackButton
onClick={() => setCreateModalOpen(true)}
sx={{
position: 'absolute',
right: CONTENT_PADDING,
top: CONTENT_PADDING,
paddingRight: '16px',
fontWeight: 'bold',
fontSize: '0.9rem',
}}
>
<Icon sx={{ marginRight: '8px' }}>add</Icon>New Tag Category
</BlackButton>
{viewModelTagCategory && (
<Box>
{viewModelTagCategory.map((tagCategory, i) => (
<Box key={tagCategory.name + i}>
<Box className="category-box" sx={{ marginTop: '16px' }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography
variant="subtitle1"
sx={{ fontWeight: 'bold' }}
>
{tagCategory.name}_{tagCategory.id}
</Typography>
<IconButton
aria-label="more"
id={`tag-category-menu-btn-${tagCategory.id}`}
aria-controls={
categoryMenuOpen
? `tag-category-menu-${tagCategory.id}`
: undefined
}
aria-expanded={categoryMenuOpen ? 'true' : undefined}
aria-haspopup="true"
data-category-num={tagCategory.id}
onClick={(e) => menuClick(e, tagCategory.id)}
>
<Icon>more_vert</Icon>
</IconButton>
<Menu
id={`tag-category-menu-${tagCategory.id}`}
MenuListProps={{
'aria-labelledby': `tag-category-menu-btn-${tagCategory.id}`,
}}
anchorEl={categoryAnchorEl}
open={categoryMenuOpen}
onClose={menuClose}
>
<MenuItem
onClick={() => openTagCategoryEditModal(idItemTag)}
>
<EditOutlinedIcon sx={{ mr: 1 }} />
<Typography>Edit</Typography>
</MenuItem>
<MenuItem
onClick={() => openTagCategoryDeleteModal(idItemTag)}
sx={{ color: 'red' }}
>
<DeleteOutlinedIcon sx={{ mr: 1 }} />
<Typography>Delete</Typography>
</MenuItem>
</Menu>
</Box>
{tagCategory.tagList.length > 0 &&
tagCategory.tagList.map((tag, j) => (
<Box key={j}>
<Box sx={{ display: 'flex', marginTop: '32px' }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
background: '#283237',
color: '#ffffff',
padding: '0 0 0 12px',
borderRadius: '20px',
fontWeight: 'bold',
marginLeft: '8px',
':first-of-type': {
marginLeft: 0,
},
}}
>
{tag.name}
<IconButton
size="small"
sx={{ color: '#ffffff' }}
>
<Icon fontSize="small">close</Icon>
</IconButton>
</Box>
<Button
sx={{
display: 'flex',
alignItems: 'center',
background: '#ffffff',
color: '#000000',
padding: '0 12px',
borderRadius: '20px',
fontWeight: 'bold',
border: '#aaaaaa 1px solid',
marginLeft: '8px',
textTransform: 'none',
':hover': {
borderColor: '#1976d2',
boxShadow: '0px 0px 0px 1px #1976d2 inset',
},
':only-child': {
marginLeft: 0,
},
}}
onClick={() => {
setCategoryIndex(i)
setTagIndex(j)
setTraitsPickerOpen(true)
}}
>
Add Traits
</Button>
</Box>
{taggedList[i] && (
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
marginTop: '8px',
width: '1000px',
}}
>
{taggedList[i][j].map((trait, k) => (
<Trait key={k} {...trait} />
))}
</Box>
)}
</Box>
))}
</Box>
<Button
onClick={() => onClickToggleUntaggedComponentsBtn(i)}
sx={{
fontSize: '0.85rem',
marginTop: '40px',
color: '#005eff',
textTransform: 'none',
}}
>
{isVisibleUntaggedList[i] ? 'Hide' : 'Show'} untagged
components
</Button>
{isVisibleUntaggedList[i] && (
<Box sx={{ width: '1000px', maxWidth: '100vw' }}>
{unTaggedList[i] &&
Object.keys(unTaggedList[i]).map((key, k) => (
<Box key={k}>
{unTaggedList[i][key].length > 0 && (
<>
<Box sx={{ fontWeight: 'bold' }}>{key}</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
}}
>
{unTaggedList[i][key].map((trait, l) => (
<Trait key={`${k}_${l}`} {...trait} />
))}
</Box>
</>
)}
</Box>
))}
</Box>
)}
</Box>
))}
</Box>
)}
<CreateTagCategoryModal
open={createModalOpen}
setOpen={setCreateModalOpen}
onCreate={addTagCategory}
/>
<TraitsPickerModal
title={
tagCategoryList[categoryIndex]
? `${tagCategoryList[categoryIndex].tagList[tagIndex].name} Traits`
: ''
}
open={traitsPickerOpen}
setOpen={setTraitsPickerOpen}
onPick={onClickTrait}
traitList={
taggedList[categoryIndex]
? taggedList[categoryIndex][tagIndex]
: []
}
/>
</Grid>
</Box>
</MainLayout>
)
}
export default Tags
My modalEdit
import * as React from 'react'
import {
Box,
Button,
Modal,
Typography,
IconButton,
TextField,
FormControlLabel,
Switch,
} from '#mui/material'
import { ModalBox } from 'components/Elements/Box/ModalBox'
import { useStaticTagsViewModelSWR } from '../../hooks/useStaticArtViewModelSWR'
import CloseIcon from '#mui/icons-material/Close'
import AddCircleOutlineIcon from '#mui/icons-material/AddCircleOutline'
import DeleteOutlineOutlinedIcon from '#mui/icons-material/DeleteOutlineOutlined'
export const TagCategoryEditModal = () => {
const { viewModel, setViewModel } = useStaticTagsViewModelSWR()
const [includeMetadataFlag, setIncludeMetadataFlag] =
React.useState<boolean>(true)
const modalClose = () => {
setViewModel({ ...viewModel, tagCategoryEditModal: { isOpen: false } })
}
return (
<Modal
open={viewModel.tagCategoryEditModal.isOpen}
onClose={modalClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<>
<ModalBox
style={{ display: 'flex', flexDirection: 'column', height: 450 }}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
mb: 2,
}}
>
<Typography variant="h4" sx={{ fontWeight: 'bold' }} gutterBottom>
Edit Tag Category
</Typography>
<IconButton onClick={modalClose}>
<CloseIcon />
</IconButton>
</Box>
<Box sx={{ mb: 3 }}>
<Typography sx={{ mb: 1 }}>Name</Typography>
<TextField sx={{ width: '100%' }} />
</Box>
<Box sx={{ mb: 3 }}>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<Typography>Tags</Typography>
<IconButton>
<AddCircleOutlineIcon />
</IconButton>
</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<TextField sx={{ width: '100%', mr: 2 }} />
<IconButton>
<DeleteOutlineOutlinedIcon />
</IconButton>
</Box>
</Box>
<Box sx={{ mb: 2 }}>
<FormControlLabel
sx={{ ml: 0 }}
value="start"
control={
<Switch
color="primary"
checked={includeMetadataFlag}
onChange={() => setIncludeMetadataFlag(!includeMetadataFlag)}
/>
}
label="Include in metadata?"
labelPlacement="start"
/>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="outlined"
sx={{ color: 'black', borderColor: '#d1d8db', marginRight: 1 }}
onClick={modalClose}
>
Cancel
</Button>
<Button variant="contained">Save</Button>
</Box>
</ModalBox>
</>
</Modal>
)
}

How to use MessageBird SMS with a form in React?

When I use MessageBird with a phone number that was hardcoded, everything goes well. The message is sent correctly and I am satisfied.
The problem is when I try to send an SMS with a form where the user needs to enter the phone number.
I am using React Hooks.
function Account() {
const { authenticate, isAuthenticated, account, chainId, logout } =
useMoralis();
const [isModalVisible, setIsModalVisible] = useState(false);
const [isAuthModalVisible, setIsAuthModalVisible] = useState(false);
const [phoneNumber, setPhoneNumber] = useState();
function phoneNumberFromForm (phone) {
setPhoneNumber (phone);
console.log(" phone: ", phoneNumber);
}
if (!isAuthenticated || !account) {
return (
<>
<div onClick={() => setIsAuthModalVisible(true)}>
<p style={styles.text}>Authenticate</p>
</div>
<Modal
visible={isAuthModalVisible}
footer={null}
onCancel={() => setIsAuthModalVisible(false)}
bodyStyle={{
padding: "15px",
fontSize: "17px",
fontWeight: "500",
}}
style={{ fontSize: "16px", fontWeight: "500" }}
width="340px"
>
<div
style={{
padding: "10px",
display: "flex",
justifyContent: "center",
fontWeight: "700",
fontSize: "20px",
}}
>
Connect Wallet
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr" }}>
{connectors.map(({ title, icon, connectorId }, key) => (
<div
style={styles.connector}
key={key}
onClick={async () => {
try {
if (connectorId) {
await authenticate({ provider: connectorId });
window.localStorage.setItem("connectorId", connectorId);
} else {
await authenticate();
}
setIsAuthModalVisible(false);
} catch (e) {
console.error(e);
}
}}
>
<img src={icon} alt={title} style={styles.icon} />
<Text style={{ fontSize: "14px" }}>{title}</Text>
</div>
))}
</div>
</Modal>
</>
);
}
return (
<>
<div style={styles.account} onClick={() => setIsModalVisible(true)}>
<p style={{ marginRight: "5px", ...styles.text }}>
{getEllipsisTxt(account, 6)}
</p>
<Blockie currentWallet scale={3} />
</div>
<Modal
visible={isModalVisible}
footer={null}
onCancel={() => setIsModalVisible(false)}
bodyStyle={{
padding: "15px",
fontSize: "17px",
fontWeight: "500",
}}
style={{ fontSize: "16px", fontWeight: "500" }}
width="400px"
>
Account
<Card
style={{
marginTop: "10px",
borderRadius: "1rem",
}}
bodyStyle={{ padding: "15px" }}
>
<Address
avatar="left"
size={6}
copyable
style={{ fontSize: "20px" }}
/>
<div style={{ marginTop: "10px", padding: "0 10px" }}>
<a
href={`${getExplorer(chainId)}/address/${account}`}
target="_blank"
rel="noreferrer"
>
<SelectOutlined style={{ marginRight: "5px" }} />
View on Explorer
</a>
</div>
<div>
<PhoneInput enableAreaCodeStretch onChange={phone => phoneNumberFromForm({phone})}/>
</div>
<Button
size="small"
type="primary"
style={{
width: "50%",
marginTop: "10px",
borderRadius: "0.5rem",
fontSize: "12px",
fontWeight: "500",
}}
onClick={async () => {
console.log(" phone number on form", phoneNumber);
messagebird.messages.create({
originator : '67528923',
recipients : '$phoneNumber.phone',
body : 'testing this function. '
},
function (err, response) {
if (err) {
console.log("ERROR:");
console.log(err);
} else {
console.log("SUCCESS:");
console.log(response);
}
});
}}
>
Verify phone number
</Button>
</Card>
<Button
size="large"
type="primary"
style={{
width: "100%",
marginTop: "10px",
borderRadius: "0.5rem",
fontSize: "16px",
fontWeight: "500",
}}
onClick={async () => {
await logout();
window.localStorage.removeItem("connectorId");
setIsModalVisible(false);
}}
>
Disconnect Wallet
</Button>
</Modal>
</>
);
}
export default Account;
Can anyone tell me what I am doing wrong?
This is the error I am getting.
Error: api error(s): no (correct) recipients found (code: 9, parameter: recipient)
I believe it must have something to do to how I am calling the recipient's number.
I am using this line.
recipients : '$phoneNumber.phone',
I am using the code above, but is it the correct way to call that object?
You guessed it right, the problem is in the place where you call MessageBird API.
messagebird.messages.create({
originator : '67528923',
recipients : '$phoneNumber.phone',
body : 'testing this function. '
},
recipients should be array of strings (phone numbers), e.g.:
{
'originator': 'YourBrand',
'recipients': [
'31612345678'
],
'body': 'Hello, world!'
}
So I think in your code you need to change it to:
messagebird.messages.create({
originator : '67528923',
recipients : [phoneNumber.phone],
body : 'testing this function. '
},

Why does liking one option like all the rest?

I am attempting to have a single user be able to choose from 1 of 2 options but when I like one they are all liked for some reason.
This is the result. I have an onClick function on the heart so that it adds the red.
https://i.stack.imgur.com/r73UB.png
Below is the code that runs when I click on the heart. I basically set the usersID into a collection called likes and save thier username
const [likes, setLikes] = useState([])
const [hasLiked, setHasLiked] = useState(false)
const likeFighter = async (e) => {
const docRef = doc(db, 'userpicks', value)
const docSnap = await getDoc(docRef)
const picksRef = doc(db, 'picks', value)
const pickSnap = await getDoc(picksRef)
let fighterID = e.target.id.toLowerCase().replace(' ', '-').replace(' ', '-')
if (hasLiked) {
await deleteDoc(doc(db, 'picks', value, 'likes', userInfo.uid))
}
else {
if (fighterID !== ' ') {
await setDoc(doc(db, 'picks', value, 'likes', userInfo.uid), {
username: userInfo.uid
})
}
}
}
This is what the database looks like. The main collection is called 'picks'. The parent collection that is.
https://i.stack.imgur.com/JVrk3.png
I also have these useEffects with snapshot listeners that run every-time a user likes a particular fighter. How can I have it so that they are only able to like one of the two fighters? If they like the left fighter they cannot like the right and vice versa.
useEffect(() => onSnapshot(collection(db, 'picks', value, 'likes'), (snapshot) =>
setLikes(snapshot.docs)
), [db, value])
useEffect(
() =>
setHasLiked(
likes.findIndex((like) => like.id === userInfo.uid) !== -1
),
[likes]
)
This is the logic that gets the fighter information from the database and renders it on the screen. I tried using the fighters name as an id so that on click it likes that particular fighter but It doesn't work.
const renderFights = fight.map((element, idx) => {
return (
<Paper elevation={0} id={element.fightID} key={idx} sx={{ width: '100%', height: '100%', display: 'flex', justifyContent: 'space-evenly' }} >
<Paper elevation={1} key={element.fighterRed} sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '50%', m: 3 }}>
<Avatar sx={{ mt: 1 }} src={element.fighterRedImage} />
<Typography variant='body1'> {element.fighterRed} </Typography>
<Typography variant='overline' sx={{ fontSize: '14px' }}> {element.fighterRedRecord} </Typography>
<Paper sx={{ display: 'flex' }} elevation={0} name={element.fightID}>
{hasLiked ?
<FavoriteIcon id={element.fighterRed} onClick={likeFighter} sx={{ cursor: 'pointer', color: 'red' }} /> :
<FavoriteBorder id={element.fighterRed} onClick={likeFighter} sx={{ cursor: 'pointer' }} />
}
{likes.length > 0 && (
<Typography>{likes.length}</Typography>)
}
</Paper>
</Paper>
<Paper elevation={0} sx={{ width: '10%', display: 'flex', flexDirection: 'column', mt: 3, mb: 3, justifyContent: 'space-evenly', alignItems: 'center' }} >
<Typography sx={{ fontSize: '10px', textAlign: 'center' }} variant='overline'> {element.weightClass}</Typography>
<Typography sx={{ fontSize: '10px', textAlign: 'center' }} variant='overline'>vs</Typography>
<Chip label="FINAL U DEC" sx={{ fontSize: '8px' }} />
</Paper>
<Paper elevation={1} key={element.fighterBlue} sx={{ display: 'flex', flexDirection: 'column', alignItems: 'center', width: '50%', m: 3 }}>
<Avatar sx={{ mt: 1 }} src={element.fighterBlueImage} />
<Typography variant='body1'> {element.fighterBlue} </Typography>
<Typography variant='overline' sx={{ fontSize: '14px' }}> {element.fighterBlueRecord} </Typography>
<Paper sx={{ display: 'flex' }} elevation={0}>
{hasLiked ?
<FavoriteIcon id={element.fighterBlue} onClick={likeFighter} sx={{ cursor: 'pointer', color: 'red' }} /> :
<FavoriteBorder id={element.fighterBlue} onClick={likeFighter} sx={{ cursor: 'pointer' }} />
}
{likes.length > 0 && (
<Typography>{likes.length}</Typography>)
}
</Paper>
</Paper>
</Paper>
)
})

ReactJS img not updating after API Call

Im learning React right now and trying to wrap my head around why my other components updated the information but my img tag has not after the second API call.
Here's my code:
export default function LandingPage() {
const [zipcode, setZipCode] = useState('');
const [loading, setLoading] = useState(false);
const [weatherData, setWeatherData] = useState();
var [cityWeatherData, setCityWeatherData] = useState([]);
var [forecast, setForcast] = useState([]);
return(
<TextField
label='Zip Code'
value={zipcode}
onChange={(e) => { setZipCode(e.target.value) }} />
<Button
sx={{ ml: 3, backgroundColor: '#5F8FFF', color: 'white', '&:hover': { color: '#5F8FFF' } }}
onClick={ () => {
currentWeather(zipcode, apiKey)
.then( async (result) => {
setLoading(true);
await sevenDayWeather(result['coord']['lon'], result['coord']['lat'], apiKey)
.then( (response) => {
response['daily'].forEach( (day) => {
console.log('day forecast: ', day);
console.log('Day Weather: ', day['weather'][0]['icon']);
setForcast( forecast => [...forecast, day['weather'][0]['icon']]);
})
});
});
}}>
Search
</Button>
{loading ?
// console.log('forecast: ', forecast)
<WeatherBox
apiKey={apiKey}
name={weatherData['name']}
lat={weatherData['coord']['lat']}
lon={weatherData['coord']['lon']}
feelsLike={weatherData['main']['feels_like']}
highestTemp={weatherData['main']['temp_max']}
lowestTemp={weatherData['main']['temp_min']}
forecast={forecast}
/> : <></>}
);}
For my WeatherBox component
export default function WeatherBox(props) {
let newDate = new Date()
let date = newDate.getDate();
let month = newDate.getMonth() + 1;
let year = newDate.getFullYear();
return (
<Box
className='retrievedInformation'
sx={{
mt: 10,
p: 5,
boxShadow: 'gray 5px 10px 10px 5px',
borderRadius: '20px',
textAlign: 'end',
backgroundImage: `url(${sunny})`,
objectFit: 'contain',
backgroundRepeat: 'no-repeat',
backgroundSize: '500px 500px'
}}>
<Typography
sx={{ fontSize: '50px' }}>
{props.name}
</Typography>
<Typography
sx={{ fontSize: '25px' }}>
Today: {month} / {date} / {year}
</Typography>
<img src={`http://openweathermap.org/img/wn/${props.forecast[0]}#2x.png`} alt='forecast image' />
<Box
display='flex'
flexDirection='row'
sx={{ textAlign: 'end', justifyContent: 'end', alignItems: 'end' }}>
<Typography
sx={{ mr: 3, fontSize: '30px', fontWeight: '300', color: 'gray' }}>
Feels Like:
</Typography>
<Typography
sx={{ fontSize: '30px' }}>
{props.feelsLike} F
</Typography>
</Box>
<Box
display='flex'
flexDirection='row'
justifyContent='end'
alignItems='end'
sx={{ textAlign: 'end' }}>
<Typography
sx={{ mr: 3, fontSize: '20px', fontWeight: '300', color: 'gray', textAlign: 'end' }}>
Highest Temperature:
</Typography>
<Typography
sx={{ fontSize: '20px', textAlign: 'end' }}>
{props.highestTemp} F
</Typography>
</Box>
<Box
display='flex'
flexDirection='row'
justifyContent='end'
alignItems='end'>
<Typography sx={{ mr: 3, fontSize: '20px', fontWeight: '300', color: 'gray', textAlign: 'end' }}>Lowest Temperature: </Typography>
<Typography sx={{ fontSize: '20px', textAlign: 'end' }}> {props.lowestTemp} F</Typography>
</Box>
<Box textAlign='end' alignItems='end' justifyContent='end'>
<Typography sx={{ mt: 5, fontSize: '30px' }}>Weather forecast for the next 7 days</Typography>
<img src={`http://openweathermap.org/img/wn/${props.forecast[1]}#2x.png`} alt='forecast image' />
<img src={`http://openweathermap.org/img/wn/${props.forecast[2]}#2x.png`} alt='forecast image' />
<img src={`http://openweathermap.org/img/wn/${props.forecast[3]}#2x.png`} alt='forecast image' />
<img src={`http://openweathermap.org/img/wn/${props.forecast[4]}#2x.png`} alt='forecast image' />
<img src={`http://openweathermap.org/img/wn/${props.forecast[5]}#2x.png`} alt='forecast image' />
<img src={`http://openweathermap.org/img/wn/${props.forecast[6]}#2x.png`} alt='forecast image' />
<img src={`http://openweathermap.org/img/wn/${props.forecast[7]}#2x.png`} alt='forecast image' />
</Box>
</Box>
);
}
My forecast array has been update as well and holding all the correct values however, the img tag in weatherbox is still not updating
Thanks for your help in advance
EDIT: Added link to codesandbox
https://codesandbox.io/s/blissful-thunder-u76vwt?file=/src/App.js
Issue
The img tag loads all the images fine for the first call however, the
problem is that when I do another zipcode and clicked search, the
texts updated, but the img tag (the weather images) did not update (
i.e. first search 91001 everything looks great, searched again for
95133, name changed to San Jose but the weather forecast images did
not update from 91001's to 95133's)
You always append forecast data to the forecast state but only reference the first 8 elements.
response["daily"].forEach((day) => {
setForcast((forecast) => [
...forecast, // <-- shallow copy persists old forecast data
day["weather"][0]["icon"]
]);
});
...
<img
src={`http://openweathermap.org/img/wn/${props.forecast[0]}#2x.png`}
alt="forecast image"
/>
...
<img
src={`http://openweathermap.org/img/wn/${props.forecast[1]}#2x.png`}
alt="forecast image"
/>
<img
src={`http://openweathermap.org/img/wn/${props.forecast[2]}#2x.png`}
alt="forecast image"
/>
<img
src={`http://openweathermap.org/img/wn/${props.forecast[3]}#2x.png`}
alt="forecast image"
/>
<img
src={`http://openweathermap.org/img/wn/${props.forecast[4]}#2x.png`}
alt="forecast image"
/>
<img
src={`http://openweathermap.org/img/wn/${props.forecast[5]}#2x.png`}
alt="forecast image"
/>
<img
src={`http://openweathermap.org/img/wn/${props.forecast[6]}#2x.png`}
alt="forecast image"
/>
<img
src={`http://openweathermap.org/img/wn/${props.forecast[7]}#2x.png`}
alt="forecast image"
/>
Solution
To resolve, you should overwrite the forecast state when updating.
setForcast(response.daily.map((day) => day.weather[0].icon));
Here's a "optimized" button click handler.
<Button
onClick={async () => {
setLoading(false);
const weather = await currentWeather(zipcode, apiKey);
setWeatherData(weather);
console.log(weather);
const forecast = await sevenDayWeather(
weather.coord.lon,
weather.coord.lat,
apiKey
);
setForcast(forecast.daily.map((day) => day.weather[0].icon));
setLoading(true);
}}
>
Search
</Button>
And for the sake of DRY-ness, mapped forecast images.
{props.forecast.map((id, index) => (
<img
key={index}
src={`http://openweathermap.org/img/wn/${id}#2x.png`}
alt="forecast image"
/>
))}

Resources