How do I refresh React state after running navigator.pop()? - javascript

While working with React Native, I have a few components that are pushed on top of each other, some of which change the state of the component below them, like so:
Social -> Groups -> Add Group
However, when I run navigator.pop() to get back to the previous component (for example, after adding a group to a user's account), the component underneath (in this case, 'Groups') won't refresh with the latest state.
What am I doing wrong here?

Turns out I was able to solve this by inserting a componentWillUpdate on the 'Groups' component, that is, whenever the Groups component updates, it triggers a loadGroupsData function:
componentWillUpdate() {
this.loadGroupsData();
}
...to which the loadGroupsData function checks for any differences, and if any are present, it loads:
loadGroupsData() {
api.getUserGroups(this.state.userId, (data) => {
data.forEach((group, index) => {
group.groupname = group.groupname;
});
if (this.state.dataSource._cachedRowCount === undefined || this.state.dataSource._cachedRowCount !== data.length) {
this.setState({
usersGroups: data.map(data => data.groupname),
dataSource: this.state.dataSource.cloneWithRows(data),
loaded: true,
});
}
});}

Related

React component doesn't re-render on first prop-change

I am new to React and I am trying to build a hangman game.
At the moment I am using a hardcoded list of words that the program can choose from. So far everything worked great, but now I am trying to reset the game and the react component that should rerender upon one click only re-renders after two clicks on the reset button and I don't know why
these are the states that I am using :
function App() {
const [numberInList, setNumberInList] = useState(0)
const randomWordsList = ["comfort", "calm", "relax", "coffee", "cozy"];
const [generatedWord, setGeneratedWord] = useState(
randomWordsList[numberInList]
);
const [generatedWordLetters, setGeneratedWordLetters] = useState(
randomWordsList[numberInList].split("").map((letter) => {
return { letter: letter.toUpperCase(), matched: false };
})
);
function resetGame(){
setNumberInList(prev => prev + 1)
setGeneratedWord(randomWordsList[numberInList])
setGeneratedWordLetters(
generatedWord.split("").map((letter) => {
return { letter: letter.toUpperCase(), matched: false };
})
);
setFalseTries(0)
}
this is the reset function I am using
within teh function every state gets updated correctly apart from the generatedWordLetters state, which only gets updated upon clicking the reset button two times.
I can't seem to solve this problem on my own, so any help is appreciated!
Please check useEffect on React. You can use boolean flag as state, put the useEffect parameters like below
React.useEffect(() => {
// here your code works
},[flag])
flag is your boolean state when it changes on reset function, your component re render

How to set or update state of Array in grand child component

I'm trying to set state of array from grand child component which is located in parent component.
I tried to use setNumberOfVar([...varsLines]); this method but it's not updating the state immediately.it updates but one step back.
My Code
const removeVar = (e, val) => {
e.preventDefault();
var varsLines = numberOfVar;
varsLines = numberOfVar.filter((item) => {
return item.postion != val;
});
varsLines = varsLines.map((item) => {
return {
...item,
postion: item.postion > val ? item.postion - 1 : item.postion,
};
});
console.log(varsLines);
setNumberOfVar([...varsLines]); // <== this line is not updating the state immediately;
console.log(numberOfVar);
};
setNumberOfVar() is an async operation and will not update state immediately.
If you want to get something like a callback when the state is updated, you can use the useEffect hook that runs on state update.
import {useEffect } from 'react';
useEffect(() => {
console.log(numberOfVar);
},[numberOfVar]);
try to do something like this
setNumberOfVar(prevState=>prevState.filter().map())
use your own conditions in filter and map and see if it's working or not.

setState in functional component don't keep value

I have a functional component that keeps a state. This state I try to manipulate using an onClick event in an SVG. The SVG is in another component and has the addAndRemoveSelectedCabin method passed to it via props. I loop through the elements in an useEffect and add an eventListener. This doesn't work. The useEffect with the selectedCabins dependency logs the new number only. It seems the state returns to the initial state after every stateChange.
This is the state and method in the parent component.
const [selectedCabins, setSelectedCabins] = useState([0]);
const addRemoveSelectedCabin = id => {
const newArr = [...selectedCabins, id];
setSelectedCabins(newArr);
}
useEffect(() => {
console.log(selectedCabins);
}, [selectedCabins])
This is how I call the method. [UPDATE]
useEffect(() =>
{
const cabins = document.querySelectorAll(".cabin");
cabins.forEach(cabin =>
{
const id = cabin.getAttributeNS(null, "id").substring(1, 5);
const found = cabinsData.find(el => el.id === id)
if (found && found.status === "available")
{
cabin.classList.add("green")
cabin.addEventListener('click', () => addRemoveSelectedCabin(id));
} else if (found && found.status === "booked")
{
cabin.classList.add("gray")
}
})
}, [])
Console:
[0]
(2) [0, "1105"]
(2) [0, "1101"]
This works if I put the onClick directly in the SVG element. Does anyone know why this is?
<rect
id="C1105"
x="749.4"
y="58.3"
className="cabin"
width="36.4"
height="19.9"
onClick={() => addRemoveSelectedCabin(1105)}
>
<title>1105</title>
</rect>
As I said in my comment, you are binding addRemoveSelectedCabin in the first render. useEffect is only executed once since you pass an empty dependency list. addRemoveSelectedCabin closes over selectedCabins which at that point in time has the value [0].
Why am I seeing stale props or state inside my function? from the React documentation has more information about this.
The solution in your case is simple: Pass a function to the setter to get the "current" state value. Don't reference the state value in the component:
const addRemoveSelectedCabin = id => {
setSelectedCabins(selectedCabins => [...selectedCabins, id]);
}
Having said that, this is still an odd thing to do in React world. You should reevaluate your assumptions that make you think you have to do it that way.
It's not all the elements that should have a click listener.
Depending on how you actually render the elements, that's easy to do. JSX/React is just JavaScript. Whether you have a condition that adds the event handler or not or whether you have a condition that sets onClick or not is basically the same.
But without a more complete example there is not much we can suggest.

How do i prevent my React component from rendering unnecessarily?

I have a react app. On this app I am rendering 10 tables. When a user makes a change to a table I only want that one table to re-render, not all 10.
To accomplish this task I have used React.useMemo() with a comparer function. Here it is:
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
const { categoryTotal: ctPrev, ...prev } = prevProps;
const { categoryTotal: ctNext, ...next } = nextProps;
if (
!ctPrev.totalPrice.eq(ctNext.totalPrice) &&
!ctPrev.totalWeight.eq(ctNext.totalWeight) &&
!ctPrev.totalWorn.eq(ctNext.totalWorn) &&
!ctPrev.totalConsumable.eq(ctNext.totalConsumable)
) {
console.log('totals did change')
return false;
}
for (var key in next) {
if (next[key] !== prev[key]) {
console.log('not equal', key);
return false;
}
}
console.log('props did not change')
return true;
}
export default React.memo(CategoryTable, areEqual);
I have verified that true is being returned for every table except the one that changes. So only that one table should re-render and not all 10 right? Wrong. Here is my flamegraph:
The name of my table component is CategoryTable. As you can see, the CategoryTable (memo) is grayed out but the subsequent CategoryTable is green and renders as does all of its children. I have confirmed that every category table is rendering by putting a console.log in the CategoryTable component.
How do I actually stop this component from re-rendering? Also does react.memo stop all components below in the tree from rendering or just the wrapped component?
React.memo return cach if the value didnt change its really usefull but in your case you can try pureComponent ,it prevent to render the children components if their props dont change

Rendering an array of html elements

I want to render an array of html elements in my component. The reason for storing the data/html in an array is because I want to be able to dynamically load a new element depending on a button-click.
This is how I want to display my array:
<div>
{this.state.steps}
</div>
This is how I initiate my component and array:
componentDidMount() {
this.createProcessStep().then(step => {
this.setState({steps: this.state.steps.concat(step)});
});
}
export function createProcessStep() {
this.setState({processStepCounter: this.state.processStepCounter += 1});
return this.addStepToArray().then(d => {
return this.reallyCreateProcessStep()
});
}
addStepToArray = () => {
const step = {
...Some variables...
};
return new Promise(resolve => {
this.setState({
stepsData: this.state.stepsData.concat(step)
}, resolve)
});
};
"stepsData" is another array that holds data (variables) belonging to each step. "steps" on the other hand, should only hold the html.
This is how one step/element looks like:
<div>
...Some Content...
<button label="+" onClick={ () => {
this.createProcessStep().then(step => {
this.setState({
steps: this.state.steps.concat(step)
});
})
}}/>
...other content...
</div>
This button within each step is responsible for loading/adding yet another step to the array, which actually works. My component displays each step properly, however react doesn't properly render changes to the element/step, which is
to say that, whenever e.g. I change a value of an input field, react doesn't render those changes. So I can actually click on the "+"-button that will render the new html element but whenever a change to this element occurs,
react simply ignores the phenotype of said change. Keeping in mind that the changeHandlers for those steps/elements still work. I can change inputfields, radioButtons, checkboxes etc. which will do exactly what it's
supposed to, however the "re-rendering" (or whatever it is) doesn't work.
Any ideas of what I'm doing wrong here? Thanks!
While you could certainly beat your approach into working, I would advise that you take more common react approach.
You make your components to correctly display themselves from the state . ie as many steps are in the state, your component will display. Than make your add button add necessary information (information, not formated html) to the state.
Here is an example how to use component N times:
const MyRepeatedlyOccuringComponent = (n) => (<p key={n}>There goes Camel {n}</p>)
const App = () => {
const camels = [1,22,333,4444,55555]
const caravan = camels.map((n) => MyRepeatedlyOccuringComponent(n))
return(<div>{caravan}</div>
}

Categories