What is wrong in this react useState hook? - reactjs

I found this component online which creates a review component but it's not working.
import { useState } from 'react';
const Star = ({filled, starId}) => (
<span star-id={starId} style={{ color: '#ff9933' }} role="button">
{filled ? '\u2605' : '\u2606'}
</span>
);
export const Rating = props => (
const [rating, setRating] = useState(typeof props.rating == 'number' ? props.rating : 0);
const [selection, setSelection] = useState(0);
const hoverOver = event => {
let val = 0;
if (event && event.target && event.target.getAttribute('star-id'))
val = event.target.getAttribute('star-id');
setSelection(val);
};
return (
<div
onMouseOut={() => hoverOver(null)}
onClick={event => setRating(event.target.getAttribute('star-id') || rating)}
onMouseOver={hoverOver}
>
{Array.from({ length: 5 }, (v, i) => (
<Star
starId={i + 1}
key={`star_${i + 1} `}
filled={selection ? selection >= i + 1 : rating >= i + 1}
/>
))}
</div>
);
It throws an error for this line:
const [rating, setRating] = useState(typeof props.rating == 'number' ? props.rating : 0);
What is wrong with it? And how can be it fixed?

I think the hook is fine, but you need to use {} around the function body:
export const Rating = props => {
const [rating, setRating] = useState((typeof props.rating === 'number') ? props.rating : 0);
const [selection, setSelection] = useState(0);
const hoverOver = event => {
let val = 0;
if (event && event.target && event.target.getAttribute('star-id'))
val = event.target.getAttribute('star-id');
setSelection(val);
};
return (
<div
onMouseOut={() => hoverOver(null)}
onClick={event => setRating(event.target.getAttribute('star-id') || rating)}
onMouseOver={hoverOver}
>
{Array.from({ length: 5 }, (v, i) => (
<Star
starId={i + 1}
key={`star_${i + 1} `}
filled={selection ? selection >= i + 1 : rating >= i + 1}
/>
))}
</div>
);
};
codesandbox
A second thing you should consider: You probably should use === instead of == for the typeof check.

Related

React Guarantee that part of function runs after DOM updates

Currently I have a textarea like this:
<textarea
onChange={handleTextAreaChange}
ref={textAreaRef as MutableRefObject<HTMLTextAreaElement>}
id={id}
value={content}
></textarea>
I am implementing some buttons to add markdown to the textarea to make it easier for the user to update and have this function for bold:
const handleBoldClick = useCallback(() => {
const selectionStart = textAreaRef.current?.selectionStart;
const selectionEnd = textAreaRef.current?.selectionEnd;
if (selectionStart && selectionEnd) {
setContent(
prevContent =>
prevContent.substring(0, selectionStart) +
'**' +
prevContent.substring(selectionStart, selectionEnd) +
'**' +
prevContent.substring(selectionEnd, prevContent.length)
);
} else {
setContent(prevContent => prevContent + '****');
// Want this to run after textarea gets updated
textAreaRef.current?.focus();
textAreaRef.current?.setSelectionRange(
content.length - 3,
content.length - 3
);
}
const changeEvent = new Event('change', { bubbles: true });
// Want to run this after textarea is updated
textAreaRef.current?.dispatchEvent(changeEvent);
}, [content]);
setContent is the setter for content which is passed to the textarea. Is there a way to guarantee the parts I've marked with comments as wanting to only run once the DOM gets updated run when I want them to?
I finagled around with things and went with this approach (gonna post the entire component, which contains some stuff irrelevant to the question):
const MarkdownTextArea = ({
value,
onBlur = () => {},
onChange = () => {},
touched = false,
error,
id,
label
}: MarkdownTextAreaProps) => {
const [content, setContent] = useState(value ?? '');
const [numberOfRows, setNumberOfRows] = useState(5);
const [numberOfCols, setNumberOfCols] = useState(20);
const [isPreview, setIsPreview] = useState(false);
const [changed, setChanged] = useState<'bold' | null>();
const textAreaRef = useRef<HTMLTextAreaElement>();
useEffect(() => {
const setColsAndRows = () => {
const newColumnsNumber = Math.floor(
(textAreaRef.current?.offsetWidth ?? 100) /
(convertRemToPixels(1.2) / 1.85)
);
setNumberOfCols(newColumnsNumber);
setNumberOfRows(calculateNumberOfRows(content, newColumnsNumber));
};
setColsAndRows();
window.addEventListener('resize', setColsAndRows);
return () => {
window.removeEventListener('resize', setColsAndRows);
};
}, [content]);
const handleTextAreaChange: ChangeEventHandler<HTMLTextAreaElement> =
useCallback(
event => {
onChange(event);
setContent(event.target.value);
setNumberOfRows(
calculateNumberOfRows(
event.target.value,
textAreaRef.current?.cols ?? 20
)
);
},
[onChange]
);
const handleBoldClick = useCallback(() => {
const selectionStart = textAreaRef.current?.selectionStart;
const selectionEnd = textAreaRef.current?.selectionEnd;
if (selectionStart && selectionEnd) {
setContent(
prevContent =>
prevContent.substring(0, selectionStart) +
'**' +
prevContent.substring(selectionStart, selectionEnd) +
'**' +
prevContent.substring(selectionEnd, prevContent.length)
);
} else {
setContent(prevContent => prevContent + '****');
}
setChanged('bold');
}, []);
if (changed && textAreaRef.current?.value === content) {
const changeEvent = new Event('change', { bubbles: true });
textAreaRef.current?.dispatchEvent(changeEvent);
if (changed === 'bold' && textAreaRef.current) {
textAreaRef.current.focus();
textAreaRef.current.selectionStart = content.length - 2;
textAreaRef.current.selectionEnd = content.length - 2;
}
setChanged(null);
}
return (
<div className={classes.container} data-testid="markdown-text-area">
<div className={classes['header']}>
<label className={classes.label} htmlFor={id}>
{label}
</label>
<Button
positive
style={{ justifySelf: 'flex-end' }}
onClick={() => setIsPreview(prev => !prev)}
>
{isPreview ? 'Edit' : 'Preview'}
</Button>
</div>
<div className={classes['text-effect-buttons']}>
<button
className={classes['text-effect-button']}
onClick={handleBoldClick}
type="button"
style={{ fontWeight: 'bold' }}
>
B
</button>
</div>
{isPreview ? (
<div className={classes['markdown-container']} id={id}>
<MarkdownParser input={content} />
</div>
) : (
<textarea
onChange={handleTextAreaChange}
className={`${classes['text-input']}${
error && touched ? ` ${classes.error}` : ''
}`}
ref={textAreaRef as MutableRefObject<HTMLTextAreaElement>}
rows={numberOfRows}
cols={numberOfCols}
onBlur={onBlur}
id={id}
value={content}
></textarea>
)}
{error && touched && (
<div className={classes['error-message']}>{error}</div>
)}
</div>
);
};
The part of the following component most relevant to answering the question is the following:
if (changed && textAreaRef.current?.value === content) {
const changeEvent = new Event('change', { bubbles: true });
textAreaRef.current?.dispatchEvent(changeEvent);
if (changed === 'bold' && textAreaRef.current) {
textAreaRef.current.focus();
textAreaRef.current.selectionStart = content.length - 2;
textAreaRef.current.selectionEnd = content.length - 2;
}
setChanged(null);
}

rc-select showing id not name, on second click on the same option

I have strange issue that rc-select picker is showing as a option id eery tyime option is selected twice. I am passing my id to url and from url, to get proper reposnse from API. And storing my category list names in redu state.
This is done in Sorting conmponent and well there is not much hee to be broke i think
const options = {
dropdownMatchSelectWidth: 350,
listHeight: 500,
dropdownAlign: { offset: [0, 1] },
className: styles.optionName,
};
export const MediaCategoryDrawer = () => {
const router = useRouter();
const params = router.query;
const dispatch = useDispatch();
const [optionValue, setOptionValue] = useState<string | undefined>("");
const pageNumber = 1;
const PAGE_SIZE = 20;
const categoriesToShow = pageNumber * PAGE_SIZE;
const { t } = useTranslation();
// const queryCheck =
// typeof params.query === "string" ? params.query.split("_")[0] : optionValue;
const mediaCategoriesSelector = (state: IAppState) => {
return state.media.mediaCategories;
};
const mediaCategories = useSelector<IAppState, IMediaCategoryListModel>(
mediaCategoriesSelector
);
useEffect(() => {
dispatch(getMediaCategories());
}, [dispatch]);
useEffect(() => {
if (!params.category) return;
const categoryNameFromUrl = mediaCategories.Entities.find(
(entity) => entity.CategoryId === params.category
)?.CategoryName;
setOptionValue(categoryNameFromUrl);
}, [params.category, mediaCategories]);
const onCategoryChange = useCallback(
(categoryId?: string) => {
const splitId = categoryId?.split("_")[0];
console.log(categoryId);
console.log(splitId);
setOptionValue(splitId);
router.replace({
search: UrlHelper.joinQueries(params, {
id: categoryId,
category: categoryId,
sort: params.sort,
}),
});
},
[params.sort]
);
const renderOption = useMemo(() => {
return mediaCategories?.Entities?.slice(0, categoriesToShow - 1).map(
(category, _) => {
if (
category.CategoryTypeCode === MediaCategoryType.Other &&
params.type === "categories"
) {
return (
<Option
className={styles.optionName}
key={category.CategoryName}
value={category.CategoryId}
id={category.CategoryId}
>
{category.CategoryName}
</Option>
);
} else if (
category.CategoryTypeCode === MediaCategoryType.Main &&
params.type === "genre"
) {
return (
<Option
className={styles.optionName}
key={category.CategoryName}
value={category.CategoryId}
id={category.CategoryId}
>
{category.CategoryName}
</Option>
);
}
}
);
}, [params.type, mediaCategories.Entities]);
return (
<div>
<Select
{...options}
placeholder={t(
"MEDIA_CATEGORY_DRAWER__ALL_GENRES_COLLECTIONS",
"All Genres & Collections"
)}
value={`${t("SELECT_CATEGORIES", "Categories: ")} ${
optionValue === undefined
? t("ALL_CATEGORIES", "All categories")
: optionValue
}`}
onChange={(e) => onCategoryChange(e)}
>
<Option key="All" value="All categories">
{t("ALL_CATEGORIES", "All categories")}
</Option>
{renderOption}
</Select>
</div>
);
};

Shorten my code to one usestate hook, I have some problems with that

const RspGame = () => {
const selection = [ROCK, PAPER, SCISSORS];
const [select, setPlayersSelect] = useState({
playerChoice: '',
computerChoice: '',
winner: ''
});
const [player, setPlayerWin] = useState(0);
const [computer, setComputerWin] = useState(0);
const PLAYERS_CHOICE = (choice) => {
const choiceP1 = choice;
const choiceP2 = selection[Math.floor(Math.random() * 3)];
const winner = COMPARE_SELECTIONS(choiceP1, choiceP2);
setPlayersSelect({
playerChoice: choiceP1,
computerChoice: choiceP2,
winner,
});
}
const COMPARE_SELECTIONS = (P1, P2) => {
if (P1 === P2) {
console.log(DRAW);
return DRAW
} else if (
(P1 === PAPER && P2 === ROCK)
||
(P1 === SCISSORS && P2 === PAPER)
||
(P1 === ROCK && P2 === SCISSORS)
) {
console.log(PLAYER_WIN);
setPlayerWin(player + 1)
return PLAYER_WIN;
} else {
console.log(COMPUTER_WIN);
setComputerWin(computer + 1)
return COMPUTER_WIN;
}
}
return (
<div className='game-wrapper'>
<span className="score">
<h1 className="playerCount">{player}</h1>
<h1>-</h1>
<h1 className="computerCount">{computer}</h1>
</span>
<span className='selection'>
<h3>{select.playerChoice}</h3>
<h3>{select.computerChoice}</h3>
</span>
<span className='btn-wrapper'>
<Button clicked={() => PLAYERS_CHOICE(ROCK)} class='btn'>ROCK</Button>
<Button clicked={() => PLAYERS_CHOICE(PAPER)} class='btn'>PAPER</Button>
<Button clicked={() => PLAYERS_CHOICE(SCISSORS)} class='btn'>SCISSORS</Button>
</span>
</div>
);
};
export default RspGame;
i'd like it to look like this:
const [select, setPlayersSelect] = useState({
playerChoice: '',
computerChoice: '',
winner: {
playerCount: 0,
computerCount: 0,
}
});
what must be changed?
May be you know how to improve my code in other ways or to do it with redux for variety! When i put setPlayersSelect(select.winner.playerCount + 1) in if statement and put {select.winner.playerCount} in player-score place, it does not work, just disappears
setPlayersSelect(select.winner.playerCount + 1)
Your select is an object, with such command you change state to a number, instead try:
setPlayersSelect(prev => {...prev, winner: {playerCount: prev.winner.playerCount + 1} });

State in redux changes but it's not updating in the component

My redux store updates perfectly
after sorting
before sorting
But the props in my component don't update even when the store has updated and so the child components still show the old values. I'm sorting my channels according to the timestamp. The sorting function updates the redux store but it still renders the non-sorted data.
the output it should be sorted but it remains the same(unsorted)
This is my component code
export const RoomItem = (props) => {
const [roomLiveStatus, setRoomLiveStatus] = useState(false);
const compareTimestamp = (t1 = 0, t2 = 0) => {
return (t1.lastMessage && t2.lastMessage) && t2.lastMessage.timestamp - t1.lastMessage.timestamp
}
const getRoomData = () => {
const { roomData, workspace } = props;
const { workspaceId } = workspace
const workspaceIdLowerCase = workspaceId.toLowerCase()
const { roomId } = roomData;
const roomIdLowerCase = roomId.toLowerCase()
firebase
.firestore()
.collection(`sessions/${workspaceIdLowerCase}/rooms`)
.doc(`${roomIdLowerCase}`)
.onSnapshot(doc => {
if (doc.exists) {
// console.log("LIVE Sessions doc: ", doc.data())
const { currentSession } = doc.data()
if (currentSession !== "") {
setRoomLiveStatus(true)
} else {
setRoomLiveStatus(false)
}
}
})
}
useEffect(() => {
getRoomData();
},[])
useEffect(() => {
// do something
getRoomData();
// console.log(props,"props of roomitem")
},[props.sortType])
const strip = (value) => {
const { user, content } = value
let name = user.name;
let firstName = name.trim().split(" ")[0]
if (value.type === 0) {
if ( (firstName.length + content.length) > 32 ) {
let completeContent = `${firstName}: ${content}`
return `${completeContent.slice(0, 32)}...`
} else {
return `${firstName}: ${content}`
}
} else if (value.type === 1) {
return <span>{firstName}: <FontAwesomeIcon icon={faCalendarAlt} className="text-theme" /> Schedule</span>
} else if (value.type === 2) {
return (
<span>{firstName}: <FontAwesomeIcon icon={faClipboard} className="text-theme" />Files</span>
);
} else if (value.type === 3) {
return <span>{firstName}: <FontAwesomeIcon icon={faPoll} className="text-theme" /> Poll</span>
} else if (value.type === 4) {
return <span>{firstName}: <FontAwesomeIcon icon={faTasks} className="text-theme" /> Quiz</span>
} else if (value.type === 6) {
if ( (firstName.length + content.length) > 32) {
let len = 32 - firstName.length;
return <span>{firstName}: <FontAwesomeIcon icon={faImage} /> {content.length > len ? content.slice(0, len) + '…' : content}</span>
} else {
return <span>{firstName}: <FontAwesomeIcon icon={faImage} /> Photo</span>
}
} else if (value.type === 7) {
if ( (firstName.length + content.length) > 32) {
let len = 32 - firstName.length;
return <span>{firstName}: <FileIcon message={value} /> {content.length > len ? content.slice(0, len) + '…' : content}</span>
} else {
return <span>{firstName}: <FileIcon message={value} /> {value.metaData && value.metaData.name}</span>
}
} else if (value.type === 8) {
return <span>{content.length > 36 ? `${content.slice(0, 36)}...` : content}</span>
} else if (value.type === 9) {
return <span>{content.length > 36 ? `${content.slice(0, 36)}...` : content}</span>
} else {
return value.type
}
}
const {
key,
currentChannel,
workspaceData,
workspace,
setCurrentChannel,
setCurrentWorkspace,
setParticipants,
resetData
} = props;
const roomData = props.roomData;
const { roomId } = roomData;
return(
<li
className={currentChannel && (roomData.roomId === currentChannel.roomId)
? "active rounded-lg py-1 m-1 bg-card-theme shadow-sm text-theme"
: "rounded-lg py-1 m-1 bg-card-theme shadow-sm text-theme"}
key={key}
onClick={() => {
setCurrentChannel({ ...roomData, roomId })
setCurrentWorkspace({ ...workspaceData, ...workspace })
setParticipants(workspace.workspaceId, roomId)
resetData()
// setLeftPanel(!this.props.displayLeftPanel);
}}
name={roomData.roomName}
active={currentChannel && (roomData.roomId === currentChannel.roomId)}
>
<div className="d-flex align-items-center p-2 w-100">
<div className={roomLiveStatus ? "liveroom" : ""}>
<img
className={roomLiveStatus ? "mr-2 rounded-circle profile-image" : "mr-2 rounded-circle"}
src={roomData.roomPic}
style={{ height: 45, width: 45 }} />
</div>
<div className="flex-grow-1">
<div className="d-flex align-items-center">
{(roomData.roomType === 1)
&& <FontAwesomeIcon
icon={faLock}
className="text-success mr-2"
size="xs" />}
<p className="mb-0 text-theme">{roomData.roomName}</p>
</div>
{roomData.lastMessage
&& <small className="text-theme text-theme-lighter">
<span>{strip(roomData.lastMessage)}</span>
</small>}
</div>
<div className="text-right align-self-start">
{/* <FontAwesomeIcon
icon={faThumbtack}
style={isPinned ? { fontSize: 12, transform: "rotate(45deg)" } : { fontSize: 12 }}
className={isPinned ? "text-theme" : "text-secondary"} /> */}
<p
className="mb-0 text-theme small text-theme-lighter"
style={{ whiteSpace: "nowrap" }}
>
{roomData.lastMessage
&& timeFromNow(roomData.lastMessage.timestamp)}
</p>
{/* Messages Notification */}
{/* <span className="text-white bg-primary smaller font-weight-bold" style={{ whiteSpace: "nowrap", borderRadius: "2px", padding: "3px 3px 3px 3px" }}>
99+</span> */}
</div>
</div>
</li>
)
}
const WorkspaceListElement = (props) => {
const [workspaceData, setWorkspaceData] = useState({});
const [loadingWorkspaceData, setLoadingWorkspaceData] = useState(true);
const [roomsDataArray, setRoomsDataArray] = useState([]);
const [sortingCount, setSortingCount] = useState(0);
const getWorkspaceData = async () => {
const { workspace } = props;
let docRef = await firebase.firestore().collection(`workspaces`).doc(`${workspace.workspaceId}`)
let workspace_data = await docRef.get()
.then(function (doc) {
if (doc.exists) {
// console.log("Document data workspace:", doc.data());
const workspaceData = doc.data()
return workspaceData;
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch(function (error) {
console.log("Error getting document:", error);
})
setWorkspaceData(workspace_data);
setLoadingWorkspaceData(false);
}
const getAllRoomsData = () => {
const { workspace, roomsVisible, setChannels } = props
let roomsArray = []
let roomsDataPromises = []
let roomsDataArray = []
getWorkspaceData()
roomsArray = workspace[roomsVisible] && Object.values(workspace[roomsVisible]).map((room, key) => (
room.roomId
))
// console.log(`roomsArray ${JSON.stringify(roomsArray)}`)
roomsDataPromises = roomsArray.map((roomId, key) => firebase.firestore().collection(`workspaces/${workspace.workspaceId}/rooms`).doc(`${roomId}`).get())
Promise.all(roomsDataPromises).then(values => {
roomsDataArray = values.map(value => {
return { ...value.data(), roomId: value.id }
})
setChannels(roomsDataArray)
})
}
const {
workspace,
_handleAddRoom,
_handleOpenWorkspaceDetails,
roomsVisible,
currentChannel,
allChannels,
searchTerm,
sortType
} = props;
const regex = new RegExp(searchTerm, "gi");
useEffect(() => {
getAllRoomsData()
},[])
useEffect(() => {
getAllRoomsData()
},[props.roomsVisible, props.workspace[props.roomsVisible]])
useEffect(() => {
getWorkspaceData()
},[props.workspace])
useEffect(() => {
console.log('sorttype changed')
switchSort(allChannels, sortType)
setSortingCount((prev) => prev + 1)
},[sortType])
const compareTimestamp = (t1 = null, t2 = null) => {
if (t1 && t2) {return t2.timestamp - t1.timestamp}
if (t1 && t2 == null) {return -1}
if (t2 && t1 == null) {return 1}
return 0
}
const compareTimestampLodashLatestFirst = (allChannels) => {
const sorted = _.orderBy(allChannels, (channel) => {
})
props.setChannels(sorted);
return (sorted);
}
const compareAlphabetLodashAtoZ = (allChannels) => {
const sorted = _.sortBy(allChannels, (channel) => channel.roomName)
// console.log('atoz')
props.setChannels(sorted)
return (sorted);
}
const compareAlphabetLodashZtoA = (allChannels) => {
const sorted = _.sortBy(allChannels, (channel) => channel.roomName)
// console.log('ztoa')
props.setChannels(sorted.reverse())
return (sorted.reverse());
}
const switchSort = (allChannels, sortType) => {
// console.log(allChannels,"allChannels")
switch (sortType) {
case 0:
return compareTimestampLodashLatestFirst(allChannels)
case 1:
return compareAlphabetLodashAtoZ(allChannels)
case 2:
return compareAlphabetLodashZtoA(allChannels)
case 3:
return compareTimestampLodashLatestFirst(allChannels)
default:
return compareTimestampLodashLatestFirst(allChannels)
}
}
// console.log(allChannels,"before return")
return(
<>
{
searchTerm && searchTerm.length > 0
? allChannels
&& allChannels
.filter(item => {
return item.roomName.match(regex) || (item.lastMessage && item.lastMessage.content && item.lastMessage.content.match(regex))
})
.sort((a, b) => switchSort(a, b, sortType))
.map((room, key) => (
<RoomItem
roomData={room}
key={key}
index={key}
currentChannel={currentChannel}
workspace={workspace}
workspaceData={workspaceData}
allChannels={allChannels}
{...props}
/>
))
: allChannels &&
allChannels.map((room, key) => {
return(
<RoomItem
roomData={room}
key={room.roomName + key}
index={key}
currentChannel={currentChannel}
workspace={workspace}
workspaceData={workspaceData}
{...props}
/>
)
})
}
</>
)
}
const mapStateToProps = state => ({
roomsVisible: state.workspace.roomsVisible,
currentChannel: state.channel.currentChannel,
allChannels: state.channel.allChannels,
platform: state.channel.platform
})
export default connect(mapStateToProps, {
setChannels,
setCurrentChannel,
setCurrentWorkspace,
setParticipants,
resetData
})(WorkspaceListElement);
Edit: I fixed it by using lodash cloneDeep. If anyone is stuck in a similar situation refer to this react-redux update item in array doesn't re-render

React Hook to display all boxes checked on a form

I've created a form and am saving the data to a json file locally. I can save all the data except for the questions with multiple selections and multiple checkboxes. It only saves the last one selected. I am trying to write a switch statement within a React Hook that is working to help save the submitted form. I keep getting an error "cannot identify type of undefined." I'm new to react and don't know what to do from here.
This is in my hooks folder:
export const useInputChange = (customValue, callback) => {
const [value, setValue] = useState(customValue ? customValue : "" || []);
const handleChange = (event) => {
var newValue;
switch (customValue.type) {
case "multipleSelection":
newValue = $("multipleSelection").find("option:checked");
break;
case "checkboxChoice":
newValue = $("checkboxChoice").find("input:checked");
break;
default:
newValue = event.target.value;
}
setValue(newValue);
if (callback) {
callback(event.target.name, newValue);
}
};
return {
value: value,
handleChange: handleChange
};
};
This is my callback in my components folder:
const callback = (name, value) => {
console.log("callback", name, value);
inlineData[name] = value;
setInlineData(inlineData);
console.log(inlineData);
};
The jquery works in the console to pull up the correct arrays.
This is the component:
export const Survey = (props) => {
const [page, setPage] = useState(1);
const [isFinalPage, setIsFinalPage] = useState(false);
const [surveyValues, setSurveyValues] = useState({});
const [loadedInputs, setLoadedInputs] = useState({});
const [question, setQuestion] = useState({});
const [inlineData, setInlineData] = useState({});
const { surveyId } = props;
const triggerBackendUpdate = () => {
console.log(question);
console.log(surveyValues);
setPage(1);
setSurveyValues({});
setQuestion({});
};
useEffect(() => {
if (surveyId) {
const inputDataFile = import(`./data_${surveyId}.json`);
inputDataFile.then((response) => {
setLoadedInputs(response.default);
});
}
});
const handleSubmit = (event) => {
event.preventDefault();
event.persist();
for (let formInput of event.target.elements) {
const isText = isTextInput(formInput.type);
console.log(formInput);
if (isText) {
surveyValues[formInput.name] = formInput.value;
question[formInput.question] = formInput.question;
}
if (formInput.type === "selectMultiple") {
let selected = [].filter.call(
formInput.options,
(option) => option.selected
);
console.log(formInput);
console.log(selected);
console.log(formInput.options.selected);
const values = selected.map((option) => option.value);
surveyValues[formInput.name] = values;
question[formInput.name] = formInput.question;
}
if (formInput.type === "checkbox") {
surveyValues[formInput.name] = formInput.value;
question[formInput.name] = formInput.question;
}
}
setQuestion(question);
setSurveyValues(surveyValues);
const nextPage = page + 1;
const inputs = props.inputs
? props.inputs.filter((inputOption) => inputOption.page ===
nextPage): [];
if (isFinalPage) {
triggerBackendUpdate();
} else {
if (inputs.length === 0) {
setIsFinalPage(true);
} else {
setPage(nextPage);
}
}
};
const callback = (name, value) => {
console.log("callback", name, value);
inlineData[name] = value;
setInlineData(inlineData);
console.log(inlineData);
};
const saveSurvey = async () => {
await fetch("/api/survey", {
method: "POST",
body: JSON.stringify(inlineData),
headers: {
"Content-Type": "application/json",
},
}).catch((error) => {
console.error(error);
});
};
const inputs = props.inputs
? props.inputs.filter((inputOption) => inputOption.page === page)
: [];
return (
<form onSubmit={handleSubmit}>
{isFinalPage !== true &&
inputs.map((obj, index) => {
let inputKey = `input-${index}-${page}`;
return obj.type === "radio" || obj.type === "checkbox" ? (
<SurveyRadioInput
object={obj}
type={obj.type}
required={props.required}
triggerCallback={callback}
question={obj.question}
defaultValue={obj.defaultValue}
name={obj.name}
key={inputKey}
/>
) : obj.type === "checkbox" ? (
<SurveyCheckboxInput
object={obj}
type={obj.type}
required={props.required}
triggerCallback={callback}
question={obj.question}
defaultValue={obj.defaultValue}
name={obj.name}
key={inputKey}
/>
) : obj.type === "select" ? (
<SurveySelectInput
className="form-control mb-3 mt-3"
object={obj}
type={obj.type}
question={obj.question}
required={props.required}
triggerCallback={callback}
defaultValue={obj.defaultValue}
name={obj.name}
key={inputKey}
/>
) : obj.type === "selectMultiple" ? (
<SurveySelectMultipleInput
className="form-control mb-3 mt-3"
object={obj}
type={obj.type}
question={obj.question}
required={props.required}
triggerCallback={callback}
defaultValue={obj.defaultValue}
name={obj.name}
key={inputKey}
/>
) : (
<SurveyTextInput
className="mb-3 mt-3 form-control"
object={obj}
type={obj.type}
question={props.question}
required={props.required}
triggerCallback={callback}
placeholder={obj.placeholder}
defaultValue={obj.defaultValue}
name={obj.name}
key={inputKey}
/>
);
})}
{isFinalPage !== true ? (
<button name="continue-btn" className="btn btn-primary my-5 mx-5">
Continue
</button>
) : (
<Link to="/thankyou">
<button
onClick={saveSurvey}
type="button"
className="btn btn-primary my-5 mx-5"
>
Submit Survey
</button>
</Link>
)}
</form>
);
};
This is in my inputs folder:
export const SurveySelectMultipleInput = (props) => {
const { object } = props;
const { value, handleChange } = useInputChange(
props.defaultValue,
props.triggerCallback
);
const inputType = isTextInput(props.type) ? props.type : "";
const inputProps = {
className: props.className ? props.className : "form-control",
onChange: handleChange,
value: value,
required: props.required,
question: props.question,
type: inputType,
name: props.name ? props.name : `${inputType}_${props.key}`,
};
console.log(value);
return (
<>
<div id={object.name}>
<h5>{props.question}</h5>
<select
{...inputProps}
name={object.name}
className={props.className}
multiple={object.multiple}
>
<option hidden value>
Select one
</option>
{object.options.map((data, index) => {
return (
<option
value={data.value}
id={`${object.name}-${index}`}
key={`${object.type}-${index}`}
className={`form-check ${props.optionClassName}`}
>
{data.label}
</option>
);
})}
</select>
</div>
</>
);
};
It's hard to tell exactly how your components and hooks behave without having an example showing their behavior and properties. Regardless, I made some assumptions and tried to answer:
First of all, what is the expected type of customValue in useInputChange? Are you expecting a string or an array? Then what is the type attribute on it that you're checking in your switch statement?
As for the jquery selector, what is multipleSelection? Is it the class name you're using for your select elements? Then your selector must start with a dot a/nd then you can get the value by calling .val method on the selected element:
newValue = $(".multipleSelection").val();
Here's a working example for multiple select elements, using your code: https://codepen.io/kaveh/pen/QWNNQMV
Note that I had to assign an arbitrary type attribute to VALUE to get it working with your switch statement.
All that being said, as I mentioned in my comment, it's recommended to use ref to access elements created by React and not other query selectors such as those you get from jquery.
https://reactjs.org/docs/refs-and-the-dom.html
https://reactjs.org/docs/hooks-reference.html#useref

Resources