I'm making MBTI app with ReactJS
But I have some problem now
When I click button i got some string ex 'E or I'
and then When it finished I got String value ex'EEINNSTTFPPJ'
so I want to change this value to 'ENTP'
How Can I make it ? 1.state
const TOTAL_SLIDES = 12
const [score, setScore] = useState(0)
const [type, setType] = useState([])
const [num, setNum] = useState(0)
const [currentSlide, setCurrentSlide] = useState(1)
const slideRef = createRef(null)
const history = useHistory()
const [mbti, setMbti] = useState('')
2.funtion
const nextSlideFir = () => {
setNum(num + 1)
setType(questions[num].answers[0].type)
setMbti(mbti + type)
setCurrentSlide(currentSlide + 1)
slideRef.current.style.transform += 'translateX(-100vw)'
}
const nextSlideSec = () => {
setNum(num + 1)
setType(questions[num].answers[1].type)
setMbti(mbti + type)
setCurrentSlide(currentSlide + 1)
slideRef.current.style.transform += 'translateX(-100vw)'
}
//I Don't know how to get same duplicate values
const mbitChecker = string => {
const words = [string]
return words.filter((item, index) => words.indexOf(item) !== index)
}
useEffect(() => {
currentSlide > TOTAL_SLIDES &&
mbitChecker(mbti) &&
history.push(`/result/${mbti}`)
})
)
Not sure what you are trying to, but you have better to have useEffect's dependency array
const extractDuplicates = (text) => {
// extractDuplicates('EEINNSTTFPPJ') -> "ENTP"
const ans = []
for (t of text) {
if (text.indexOf(t) !== text.lastIndexOf(t)) {
if (ans.indexOf(t) < 0) {
ans.push(t)
}
}
}
return ans.join('')
}
useEffect(() => {
currentSlide > TOTAL_SLIDES && history.push(`/result/${mbti}`)
console.log(`${mbti}`)
mbitChecker()
}, [mbti])
Related
I have a problem with the localStorage in my application. When I add items to a list of "favorites" they are stored without any problem in the localStorage, they can even be deleted by clicking them again.
But when I refresh the page, my application doesn't read that these items are in the favorites list and therefore doesn't mark them. Also, when I add a new item to the favorites list it causes it to delete everything from localStorage and start over.
Here's a gif of the localStorage view
Here's the code:
import React, { useState, useEffect } from 'react';
import SearchBar from '../../SearchBar/SearchBar.js';
import FiltersBox from '../FiltersBox/FiltersBox.js';
import { getItems } from '../../../Database/Database.js';
import './ItemsContainer.css';
function ItemsContainer() {
const [items, setItems] = useState([]);
const [search, setSearch] = useState('');
const [favoriteItems, setFavoriteItems] = useState([]);
let localItems = localStorage.getItem('Favorite Items');
const [sortPrice, setSortPrice] = useState('');
const [filterCategory, setFilterCategory] = useState('');
const addItemToFavorites = item => {
let existentItem = favoriteItems.find(favItem => favItem.id === item.id);
if (existentItem) {
let filterTheExistentItem = favoriteItems.filter(
favItem => item.title !== favItem.title
);
setFavoriteItems(filterTheExistentItem);
let stringItems = JSON.stringify(filterTheExistentItem);
localStorage.setItem('Favorite Items', stringItems);
} else {
setFavoriteItems([...favoriteItems, item]);
let stringItems = JSON.stringify([...favoriteItems, item]);
localStorage.setItem('Favorite Items', stringItems);
}
};
const filteredItemsList = () => {
let newItemList = [];
newItemList = items.filter(item => {
if (filterCategory !== '' && filterCategory !== 'none') {
return item.category === filterCategory;
} else {
return item;
}
});
if (sortPrice === 'ascending') {
return newItemList.sort((a, b) => (a.price > b.price ? 1 : -1));
} else if (sortPrice === 'descending') {
return newItemList.sort((a, b) => (b.price > a.price ? 1 : -1));
} else {
return newItemList;
}
};
function onSortSelected(sortValue) {
setSortPrice(sortValue);
}
function onCategorySelected(categoryValue) {
setFilterCategory(categoryValue);
}
useEffect(() => {
getItems().then(res => setItems(res));
}, []);
useEffect(() => {
let xd = JSON.parse(localItems);
console.log(xd);
}, [localItems]);
return (
<div>
<SearchBar setSearch={setSearch} />
<FiltersBox
items={items}
setItems={setItems}
onSortSelected={onSortSelected}
onCategorySelected={onCategorySelected}
/>
<div>
{filteredItemsList()
.filter(item =>
search.toLowerCase() === ''
? item
: item.title.toLowerCase().includes(search)
)
.map(item => (
<div key={item.id}>
<div>{item.title}</div>
<button
className={favoriteItems.includes(item) ? 'si' : 'no'}
onClick={() => addItemToFavorites(item)}>
Add to favorites
</button>
</div>
))}
</div>
</div>
);
}
export default ItemsContainer;
And here I leave a GIF with a continuous console.log of the localStorage:
I tried everyrhing, and I don't know what is happening.
You're retrieving your items in localItems and... you do nothing with this variable. You should initialize your state favoritesItems with your local storage
const getItemsFromLocalStorage = () => {
const items = localStorage.getItem('Favorite Items');
return items ? JSON.parse(items) : [];
}
const [favoriteItems, setFavoriteItems] = useState(getItemsFromLocalStorage())
This is where the culprit is:
const [favoriteItems, setFavoriteItems] = useState([]);
let localItems = localStorage.getItem('Favorite Items');
You load localStorage into localItems, but you expect it to be in favoriteItems, where you have never assigned it. You would need to specify the item of localStorage as the initial state, like:
let localItems = localStorage.getItem('Favorite Items');
const [favoriteItems, setFavoriteItems] = useState(localItems ? localItems : []);
I am creating to-do app in react and for the id of task i am using generator function. But This generator function is giving value 0 everytime and not incrementing the value.I think the reason for issue is useCallback() hook but i am not sure what can be the solution.How to solve the issue?Here i am providing the code :
import DateAndDay, { date } from "../DateAndDay/DateAndDay";
import TaskList, { TaskProps } from "../TaskList/TaskList";
import "./ToDo.css";
import Input from "../Input/Input";
import { ChangeEvent, useCallback, useEffect, useState } from "react";
function ToDo() {
const [inputShow, setInputShow] = useState(false);
const [valid, setValid] = useState(false);
const [enteredTask, setEnteredTask] = useState("");
const [touched, setTouched] = useState(false);
const [tasks, setTasks] = useState<TaskProps[]>(() => {
let list = localStorage.getItem("tasks");
let newdate = String(date);
const setdate = localStorage.getItem("setdate");
if (newdate !== setdate) {
localStorage.removeItem("tasks");
}
if (list) {
return JSON.parse(list);
} else {
return [];
}
});
const activeHandler = (id: number) => {
const index = tasks.findIndex((task) => task.id === id);
const updatedTasks = [...tasks];
updatedTasks[index].complete = !updatedTasks[index].complete;
setTasks(updatedTasks);
};
const clickHandler = () => {
setInputShow((prev) => !prev);
};
const input = inputShow && (
<Input
checkValidity={checkValidity}
enteredTask={enteredTask}
valid={valid}
touched={touched}
/>
);
const btn = !inputShow && (
<button className="add-btn" onClick={clickHandler}>
+
</button>
);
function checkValidity(e: ChangeEvent<HTMLInputElement>) {
setEnteredTask(e.target.value);
}
function* idGenerator() {
let i = 0;
while (true) {
yield i++;
}
}
let id = idGenerator();
const submitHandler = useCallback(
(event: KeyboardEvent) => {
event.preventDefault();
setTouched(true);
if (enteredTask === "") {
setValid(false);
} else {
setValid(true);
const newtitle = enteredTask;
const newComplete = false;
const obj = {
id: Number(id.next().value),
title: newtitle,
complete: newComplete,
};
setTasks([...tasks, obj]);
localStorage.setItem("setdate", date.toString());
setEnteredTask("");
}
},
[enteredTask, tasks, id]
);
useEffect(() => {
const handleKey = (event: KeyboardEvent) => {
if (event.key === "Escape") {
setInputShow(false);
}
if (event.key === "Enter") {
submitHandler(event);
}
};
document.addEventListener("keydown", handleKey);
return () => {
document.removeEventListener("keydown", handleKey);
};
}, [submitHandler]);
useEffect(() => {
localStorage.setItem("tasks", JSON.stringify(tasks));
}, [tasks]);
return (
<div className="to-do">
<DateAndDay />
<TaskList tasks={tasks} activeHandler={activeHandler} />
{input}
{btn}
</div>
);
}
export default ToDo;
useCallBack()'s is used to memorize the result of function sent to it. This result will never change until any variable/function of dependency array changes it's value. So, please check if the dependencies passed are correct or if they are changing in your code or not ( or provide all the code of this file). One of my guess is to add the Valid state as dependency to the array
It's because you are calling the idGenerator outside of the useCallback, so it is only generated if the Component is re-rendered, in your case... only once.
Transfer it inside useCallback and call it everytime the event is triggered:
// wrap this on a useCallback so it gets memoized
const idGenerator = useCallback(() => {
let i = 0;
while (true) {
yield i++;
}
}, []);
const submitHandler = useCallback(
(event: KeyboardEvent) => {
event.preventDefault();
let id = idGenerator();
// ... rest of logic
},
[enteredTask, tasks, idGenerator]
);
If you're using the generated id outside the event handler, store the id inside a state like so:
const idGenerator = useCallback(() => {
let i = 0;
while (true) {
yield i++;
}
}, []);
const [id, setId] = useState(idGenerator());
const submitHandler = useCallback(
(event: KeyboardEvent) => {
event.preventDefault();
let newId = idGenerator();
setId(newId)
// ... rest of logic
},
[enteredTask, tasks, id, idGenerator]
);
I have got some states. I have got one button, which starts setInterval 1 second tick interval. Inside this setInterval I set these states.
let startGame;
const Cat = () => {
const [name, setName] = useState(null);
let [foodLevel, setFoodLevel] = useState(10);
let [healthLevel, setHealthLevel] = useState(10);
const [warning, setWarning] = useState('');
const [intervalId, setIntervalId] = useState(0);
const [value, setValue] = useState('');
const onChangeHandler = (event) => {
const { value } = event.target;
setValue(value);
};
const checkZeroFoodLevel = useCallback(() => {
return foodLevel <= 0;
}, [foodLevel]);
const tick = () => {
startGame = setInterval(() => {
if (!checkZeroFoodLevel()) {
setFoodLevel((prevFoodLevel) => prevFoodLevel - 1);
console.log(foodLevel); //foodLevel=10 every tick
} else {
setHealthLevel((prevHealthLevel) => prevHealthLevel - 1);
console.log(healthLevel);//healthLevel=10 every tick
}
}, 1000);
setIntervalId(startGame);
};
const startClickHandler = () => {
setName(value);
tick();
};
if (healthLevel === 0) {
clearInterval(intervalId);
}
return(...)
};
But when I click on this button, and output in a console my foodLevel state inside setInterval callback, this state does not update, but in a render is work fine. And when I output that from outside, it is updating as expect.
I found example with useRef():
let startGame;
const Cat = () => {
const [name, setName] = useState(null);
let [foodLevel, setFoodLevel] = useState(10);
let [healthLevel, setHealthLevel] = useState(10);
const currentFood = useRef(foodLevel);
currentFood.current = foodLevel;
const [warning, setWarning] = useState('');
const [intervalId, setIntervalId] = useState(0);
const [value, setValue] = useState('');
const onChangeHandler = (event) => {
const { value } = event.target;
setValue(value);
};
const checkZeroFoodLevel = useCallback(() => {
return currentFood.current <= 0;
}, [currentFood]);
const tick = () => {
startGame = setInterval(() => {
if (!checkZeroFoodLevel()) {
setFoodLevel((prevFoodLevel) => prevFoodLevel - 1);
console.log(currentFood);
} else {
setHealthLevel((prevHealthLevel) => prevHealthLevel - 1);
console.log(healthLevel);
}
}, 1000);
setIntervalId(startGame);
};
const startClickHandler = () => {
setName(value);
tick();
};
if (healthLevel === 0) {
clearInterval(intervalId);
}
return (...);
}
This is working fine. But I think that it is overhead. If I will have many states, I must create ref for each of them. I think, that it is not correct.
Can somebody explain to me why inside the function setInterval a state does not update? Can I do it without using useRef?
I am trying to figure out how i can use the handleMouseEnter/Leave event to pause/continue the setTimeout. The rest of the code appears to be working fine for me.
function Education({ slides }) {
const [current, setCurrent] = useState(0);
const length = slides.length;
const timeout = useRef(null);
const [isHovering, setIsHovering] = useState(false);
useEffect(() => {
const nextSlide = () => {
setCurrent((current) => (current === length - 1 ? 0 : current + 1));
};
timeout.current = setTimeout(nextSlide, 3000);
return function () {
if (timeout.current) {
clearTimeout(timeout.current);
}
};
}, [current, length]);
function handleMouseEnter(e) {
setIsHovering(true);
console.log("is hovering");
}
function handleMouseLeave(e) {
setIsHovering(false);
console.log("not hovering");
}
}
Hey there you can do this by this simple implementation.
const {useEffect, useState, useRef} = React;
const Education = () => {
const slides = [1,2,3,4,5,6];
const [current, setCurrent] = useState(0);
const length = slides.length;
const timeout = useRef(null);
const [isHovering, setIsHovering] = useState(false);
useEffect(() => {
const nextSlide = () => {
setCurrent((current) => (current === length - 1 ? 0 : current + 1));
};
if ( !isHovering)
timeout.current = setTimeout(nextSlide, 2000);
return function () {
if (timeout.current) {
clearTimeout(timeout.current);
}
};
}, [current, length, isHovering]);
function handleMouseEnter(e) {
// stop the timeout function to be set
setIsHovering(true);
// clear any existing timeout functions
if ( timeout.current ){
clearTimeout(timeout.current);
}
}
function handleMouseLeave(e) {
// to trigger the useeffect function
setIsHovering(false);
}
return(
<div>
{
slides.map( (s, i) => {
if ( i === current){
return <div key={i} style={{padding:"2em", backgroundColor:"gray", fontSize:"2em"}}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>{s}</div>
}
})
}
</div>
)
}
ReactDOM.render(<Education />, document.querySelector("#app"))
You can check out in JsFiddle
I'm trying to convert the following setState function to React Hooks
const setUserAnswer = (answer) => {
this.setState((state) => ({
answersCount: {
...state.answersCount,
[answer]: (state.answersCount[answer] || 0) + 1
},
answer: answer
}));
Hope it will help:
const [answersCount, setAnswersCount] = useState(<default_value>);
const [answer, setAnswer] = useState(<default_value>);
const setUserAnswer = (answer) => {
setAnswersCount(previousState => {
return {
...previousState,
[answer]: (previousState[answer] || 0) + 1
}
})
setAnswer(answer)
}