No update component react hooks context - reactjs

The function "AdicionaItem" not reload the map of component "Items.tsx" why?
<Itens.tsx>
const { transacao } = useContext(TransacaoContext);
return (
transacao.itens.map(({descricao}: TransacaoItens) => (<h1>{descricao}</h1>)
);
<TransacaoContext.tsx>
const [transacao, setTransacao] = useState<Transacao>(transacaoInicial);
function AdicionaItem(item: TransacaoItens) {
let novosValores = transacao;
novosValores.itens = [...novosValores.itens, item];
setTransacao(novosValores);
}
<Consulta.tsx>
const { AdicionaItem } = useContext(TransacaoContext);
function Adiciona(){
AdicionaItem({descricao: "Teste"});
};

Your useState hook is outside of your functional component. Try moving it inside of AdicionaItem:
function AdicionaItem(item: TransacaoItens) {
const [transacao, setTransacao] = useState<Transacao>(transacaoInicial);
let novosValores = transacao;
novosValores.itens = [...novosValores.itens, item];
setTransacao(novosValores);
}

Related

React function on pageload

I want the heartsDisplay function call on pageload, but doing it like that causes an error. It works only with on click. How do I do this in React?
Or maybe there is a way to add default value for hearts in useState hook?
import React, { useState } from 'react'
import './App.css';
var heartsNum = 3;
const App = () => {
const [hearts, setHearts] = useState("");
var Score = 0;
var customColor = {color: 'red'};
const heartsDisplay = () => {
if (heartsNum === 3) {
setHearts("Hearts: ❤❤❤");
} else if (heartsNum === 2) {
setHearts("Hearts: ❤❤");
} else if (heartsNum === 1) {
setHearts("Hearts: ❤");
} else if (heartsNum < 1) {
setHearts("Hearts: ");
}
};
heartsDisplay();
const changeHearts = () => {
heartsNum = heartsNum - 1;
console.log(heartsNum);
heartsDisplay();
}
return (
<div>
<h3 className='hearts'>{hearts}</h3>
<button className='col1' onClick={changeHearts}>Click</button>
</div>
)
}
export default App
useEffect(()=>{
heartsDisplay();
},[]);
Call your function inside useEffect() hook
The useEffect Hook allows you to perform side effects in your components.
Some examples of side effects are: fetching data, directly updating the DOM, and timers.
useEffect accepts two arguments. The second argument is optional.
useEffect(<function>, <dependency>)
https://reactjs.org/docs/hooks-effect.html
import React, { useState } from 'react'
import './App.css';
var heartsNum = 3;
const App = () => {
const [hearts, setHearts] = useState("");
var Score = 0;
var customColor = {color: 'red'};
const heartsDisplay = () => {
if (heartsNum === 3) {
setHearts("Hearts: ❤❤❤");
} else if (heartsNum === 2) {
setHearts("Hearts: ❤❤");
} else if (heartsNum === 1) {
setHearts("Hearts: ❤");
} else if (heartsNum < 1) {
setHearts("Hearts: ");
}
};
call the function inside useEffect hook with no deps to run this function one time to trigger when a change in state or props put that state or props in deps array if you want to trigger the function before unmount return a function in useEffect callback do it in that function if you call the function openly in the component function it will call in all render
useEffect(() => {
heartsDisplay();
},[]);
const changeHearts = () => {
heartsNum = heartsNum - 1;
console.log(heartsNum);
heartsDisplay();
}
return (
<div>
<h3 className='hearts'>{hearts}</h3>
<button className='col1' onClick={changeHearts}>Click</button>
</div>
)
}
export default App
You are misunderstanding the use of useState. Default value for useState is the default value for the hearts variable.
What you are looking for is probably the useEffect hook.
It's default behavior is
similar to componentDidMount and componentDidUpdate
which basically leads to on page load behavior.
import React, { useState, useEffect } from 'react'
import './App.css';
var heartsNum = 3;
const App = () => {
const [hearts, setHearts] = useState("");
var Score = 0;
var customColor = {color: 'red'};
useEffect(() => {
heartsDisplay();
},[]);
const heartsDisplay = () => {
if (heartsNum === 3) {
setHearts("Hearts: ❤❤❤");
} else if (heartsNum === 2) {
setHearts("Hearts: ❤❤");
} else if (heartsNum === 1) {
setHearts("Hearts: ❤");
} else if (heartsNum < 1) {
setHearts("Hearts: ");
}
};
const changeHearts = () => {
heartsNum-=1;
console.log(heartsNum);
heartsDisplay();
}
return (
<div>
<div></div>
<h3 className='hearts'>{hearts}</h3>
<button className='col1' onClick={changeHearts}>Click</button>
</div>
)
}
export default App

Memoize return value of custom hook

I have a custom React hook something like this:
export default function useLocations(locationsToMatch) {
const state = useAnotherHookToGetStateFromStore();
const { allStores } = state.locations;
const allLocations = {};
allStores.forEach((store) => {
const { locationId, locationType } = store;
const isLocationPresent = locationsToMatch.indexOf(locationId) !== -1;
if (isLocationPresent && locationType === 'someValue') {
allLocations[locationId] = true;
} else {
allLocations[locationId] = false;
}
});
return allLocations;
}
When I use above hook inside my React component like this:
const locations = useLocations([908, 203, 678]) // pass location ids
I get a max call depth error due to infinite rendering. This is because I have some code inside my component which uses useEffect hook like this:
useEffect(() => { // some code to re-render component on change of locations
}, [locations])
So I tried to wrap my return value in useLocations hook inside a useMemo like this:
export default function useLocations(locationsToMatch) {
const state = useAnotherHookToGetStateFromStore();
const { allStores } = state.locations;
const allLocations = {};
const getStores = () => {
allStores.forEach((store) => {
const { locationId, locationType } = store;
const isLocationPresent = locationsToMatch.indexOf(locationId) !== -1;
if (isLocationPresent && locationType === 'someValue') {
allLocations[locationId] = true;
} else {
allLocations[locationId] = false;
}
});
return allLocations;
};
return useMemo(() => getStores(), [locationsToMatch, state]);
}
But this still causes infinite re-rendering of the consuming component. So how can I return a memoized value from my custom hook useLocations to prevent infinite re-rendering?

React hook setState, setTimeout memory leak

import React, { useState, useEffect, useRef } from 'react';
import styles from './TextAnimation.module.scss';
const TextAnimation = () => {
const [typedText, setTypedText] = useState([
"Welcome to Byc",
"Change your Life"
]);
const [value, setValue] = useState();
const [inType, setInType] = useState(false);
let attachClasses = [styles.Blink];
if(inType) {
attachClasses.push(styles.Typing)
}
const typingDelay = 200;
const erasingDelay = 100;
const newTextDelay = 5000;
let textArrayIndex = 0;
let charIndex = 0;
const type = () => {
if(charIndex < typedText[textArrayIndex].length + 1) {
setValue(typedText[textArrayIndex].substring(0, charIndex));
charIndex ++;
setTime();
} else {
setInType(false);
setTimeout(erase, newTextDelay);
}
};
const setTime = () => {
setTimeout(type, typingDelay);
};
const erase = () => {
if(charIndex > 0) {
setValue(typedText[textArrayIndex].substring(0, charIndex - 1));
charIndex --;
setTimeout(erase, erasingDelay);
} else {
setInType(false);
textArrayIndex ++;
if(textArrayIndex >= typedText.length) {
textArrayIndex = 0;
}
setTimeout(type, newTextDelay - 3100);
}
};
useEffect(() => {
type();
}, [])
return (
<div className={styles.TextAnimation}>
<span className={styles.Text} >{value}</span><span className={attachClasses.join(' ')} > </span>
</div>
);
};
export default TextAnimation;
I'am trying to make text animation, but i got an message just like this...
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
How can i fix it?
You need to clear timeouts when your component unmounts, otherwise maybe a timeout will run after the component is unmounted.
To do that :
store the return value of each timeout in a list in some ref (with React.useRef for example)
return a callback in useEffect that clears the timeouts with clearTimeout(<return value of setTimeout>)

React-Mobx 2020. inject hooks and useObserver

When I get the data (view) from useStore, I have to write all the way to this (view: myStore.menu.view) and still wrap it all in useObserver. Is there a way to shorten the code, but still keep the logic the same? I use Mobx and React Hooks.
Thanks in advance!
function useBasketStore() {
const { myStore } = useStore(['exampleStore']);
return useObserver(() => ({
view: myStore.menu?.view,
}));
}
const BasketScreen = () => {
const { view } = useBasketStore();
......
}
I think no way. Only the case if your component wrapped by observer. Then you can just use data:
function useBasketStore() {
const { myStore } = useStore(['exampleStore']);
return {
view: myStore.menu?.view,
};
}
const BasketScreen = () => {
const { view } = useBasketStore();
......
}
export default observer(BasketScreen)

How to call useSelector inside callback

This is a follow up question to this question:
How to call useDispatch in a callback
I got a React component which needs to receive information from redux in its props. The information is taken using a custom hook.
This is the custom hook:
export function useGetData(selectorFunc)
{
return type =>
{
if(!type)
{
throw new Error("got a wrong type");
}
let myData = selectorFunc(state =>
{
let res = JSON.parse(sessionStorage.getItem(type));
if(!res )
{
res = state.myReducer.myMap.get(type);
}
return res;
});
return myData;
}
}
Based on the answer for the linked question, I tried doing something like this:
function Compo(props)
{
const getDataFunc = useGetData(useSelector);
return <MyComponent dataNeeded = { getDataFunc(NeededType).dataNeeded } />
}
but I get an error because an hook can not be called inside a callback.
How can I fix this issue?
Don't pass the selector, just use it.
Also, according to your logic, you should parse the storage key outside the selector.
export function useDataFunc() {
const myData = useSelector(state => myReducer.myMap);
const getDataFunc = type => {
const resByData = myData.get(type);
try {
// JSON.parse can throw an error!
const res = JSON.parse(sessionStorage.getItem(type));
} catch (e) {
return resByData;
}
return res ? res : resByData;
};
return getDataFunc;
}
function Compo(props) {
const getDataFunc = useDataFunc();
return <MyComponent dataNeeded={getDataFunc(NeededType).dataNeeded} />;
}
I think it should be like,
const myData = useSelector(state => state.myReducer.myMap);

Resources