Creating new state variables outside of constructor - reactjs

I am trying to make multiple state variable names from data in an array that is already a state variable. The number of variables needed can change from page load to page load so I can't really initialize them right away
I've set up a function below that I believe should do what I intend for it to, but I can't figure out where to place it in the component.
makeButtons() {
this.state.Buttons.forEach((button) => {
let key = button.fields.Label
this.setState({ [key]: '' })
});
}
I've tried calling it in render(), but that just gives me a infinite loop error, which makes sense. Any ideas?
EDIT: Tried calling in componentDidMount() but that doesn't seem to work either. Not sure what the issue would be there. Code below
base('ButtonInfo').select({view: 'Grid view' }).eachPage(
(Buttons, fetchNextPage) => {
this.setState({
Buttons
});
fetchNextPage();
}
);
this.state.Buttons.forEach((button) => {
let key = button.fields.Label
this.setState({ [key]: '' })
});
EDIT: Answer is in the comments below, I needed to add await before the api call and make componentDidMount() async.

How about doing it in componentDidMount()? This method is called only once as the after the app is rendered for the first time, and is often used to initialize the app with some data.

Related

Accessing this object in componentdidmount

I'm having a problem with accessing/triggering functions from componentDidMount in React. All this references seem to be undefined even if I try binding the method in the constructor like this:
this.componentDidMount = this.componentDidMount.bind(this);
Here is a part of the code; I'm accessing events on leaflet maps and printing map boundaries to the console - which works fine (and that's the reason I need to use componentDidMount)
componentDidMount(){
let mapInst = this.refs.map.leafletElement;
mapInst.on('moveend', function () {
console.log(mapInst.getBounds())
});
mapInst.on('dragend', function () {
console.log(mapInst.getBounds())
});
}
Now I would like to pass those boundaries to state parameters or launch a function on callback to a parent element.
updateParent = (newBounds) => {
this.props.parentCallback({'bounds': newBounds});
}
However whatever construction I try, any function in higher scope is always undefined. It seems I cannot access neither updateParent(mapInst.getBounds()) nor this.updateParent(mapInst.getBounds()) from within componentDidMount.
Does anybody have any idea what the optimal construction in such cases is?
Full code for reference: https://pastebin.com/gQqazSCs
I think you want to use leaflet's whenReady callback.
handleMapReady = ({ target }) => {
this.setState({ leafletMapElt: target });
};
...
<Map whenReady={this.handleMapReady}>...</Map>
I don't think the map is guaranteed to be in a "ready" state in componentDidMount
Make callback function of mapInst.on to arrow function and then try to access updateParent like this mapInst.on('moveend', ()=> { this.updateParent(mapInst.getBounds()) });
Arrow function will take its surrounding this

React dev tools show empty state, console shows data

I'm having a strange issue with state in my React app. I set initial state as an empty array and in the componentDidMount() method I retrieve some data from Firebase. I put the JSON objects into a new array and call setState(), passing it the new array.
The problem is that it doesn't seem to be updating state. From what I can tell:
Render() is being called after setState
My callback on setState() is being fired
When I console.log the array that I set the state to, it looks fine
The strangest thing, when I inspect state in the Chrome React Devtools, it shows empty but for some reason I can print the array to the console using $r.state.nameOfMyObject
If I change some other piece of state directly from the dev tools, the app immediately renders and finally displays the piece of data I've been struggling with all along.
I thought maybe there was some issue with updating the array; maybe the diffing algorithm didn't go deep enough to see that the data inside the array changed. To test this, I tried to set the initial state of the object in question to null, but then set off errors throughout the app that it was trying to call various array methods on null.
I've tried walking through the app, console logging each step, but I can't find the issue.
Snippet of initial state:
state = {
fullSchedule: [],
currentSet: [],
aCoupleOfOtherObjects,
}
componentDidMount():
componentDidMount() {
const activities = [];
const projectReference = firestoreDB.collection("project").doc("revision001").collection("activities");
projectReference.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
activities.push(doc.data());
});
});
console.log(activities);
this.setState({fullSchedule: activities});
this.setState({currentSet: activities}, () => {
console.log("State updated from DB");
});
console.log("called setstate");
}
I can't tell why the setState() method doesn't seem to be setting the state, any ideas?
Thanks!
projectReference.get() is asynchronous, and you are trying to set the state right after you call it, which won't work.
try setting the state inside then callback:
projectReference.get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
activities.push(doc.data());
});
this.setState({fullSchedule: activities, currentSet: activities});
});
This should give you a better idea of what's going on.

How to make onClearFilter to re-render originally received data?

Hello I have been stuck here for more than two days and have been trying, but now really confused and need advise/help.
I am working on a create-react-app (using Giphy API) that has a search bar and when the User searches for a gif renders like 10 gifs. I have added a "sort" button that sorts the gifs from newest to oldest. Now I am trying to add a "clear filter" button that would re-render the gifs before it was sorted. This is what I currently have:
class App extends Component {
constructor() {
super();
this.state = {
query: '',
gifs: [],
isSearching: true,
isDataSorting: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.onSortByDate = this.onSortByDate.bind(this);
this.onClearFilter = this.onClearFilter.bind(this);
}
handleChange(evt) {
this.setState({ query: evt.target.value });
}
// ajax using axios to giphy searchendpoint happens here and is working
async handleSubmit(evt) {
evt.preventDefault();
try {
// AJAX here with API key
const { data } = await axios.get(searchEndpoint);
this.setState({
gifs: data.data,
isSearching: false,
isDataSorting: false
});
} catch(err) {
console.error(err);
}
this.setState({ query: '' });
}
// this method is working great
// when the sort btn is clicked isDataSorting becomes "true"
// and in AllGifs component I have a conditional statement that
// would render the sorted gifs (using utility/helper method)
// if "false," just renders how data/gifs comes in from endpoint
onSortByData() {
this.setState({ isDataSorting: !this.state.isDataSorting});
}
// I am stuck here... I want to "clear" the sort that happened with onSortByData()
onClearFilter() {
console.log('Clear was clicked!');
// initially, I had this hoping that it would re-render with
// isDataSorting as "false" in AllGifs component...
this.setState({ isDataSorting: false });
}
// isDataSorting, onSortByDate, and onClearFilter are all being
// passed down as props to my child component AllGifs
render() {...}
}
I am stuck on the onClearFilter(). After the User clicks "sort" and onSortByData() changes to true and the utility method sorts the gifs by newest to oldest, I want the User to click "clear" and get the originally received data order or the data before the "sort."
Thank you so much everyone for taking the time to read this.
Any advise, tips, or help would be AWESOME!
It’s difficult to pinpoint exactly what is going wrong here but I suspect that this issue may have something to do with the way in which you are sorting.
When you render the component you need to make a copy of your GIF array and sort that rather than sort the state object in place. You’d need something like:
const renderedGIFs = this.state.gifs.slice()
And then sort the rendered GIFs if the relevant flag is set. If the flag is false the array will be passed unsorted to the render function. But the crucial thing is that your state always contains the original array fetched from Giphy, which means you can always revert to it if you need to.

injecting a url parameter into an an API call in react native

I'm passing some params from a page to the other to make an API call. What i've notice is when i hard code the value i'm suppose to get into the http call, i get the desired results but when i inject the value from the params into the call, if fails to work. so i thought the params wasn't able to pass till i did an alert in the render function and what i realized was the alert prompts twice, the first one is empty and the second prompt brings the value from the previous page, so then meaning in my componentDidMount, the call
state = {
modalVisible: false,
posts : [],
itemID: ''
}
componentDidMount = () => {
const { navigation } = this.props;
this.setState({itemID : navigation.getParam('itemID')})
api.get('/items/'+`${this.state.itemID}`+'/test_items_details/'+`${userID}`+'/posts').then((response) => {
let data = response.data.data
this.setState({posts: data})
console.log(JSON.stringify(this.state.posts))
})
}
As per the docs, it states
setState() does not always immediately update the component. It may batch or defer the update until later. This makes reading this.state right after calling setState() a potential pitfall. Instead, use componentDidUpdate or a setState callback (setState(updater, callback)), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the updater argument below.
Your code snippet is trying to access the state variable before it has in fact been updated.
You can do two things here:
Simply use navigation.getParam('itemID') so now your URL becomes /items/${navigation.getParam('itemID')}/test_item_details/${userID}/posts.
Use the setState callback, the snippet is added below.
Snippet:
this.setState({ itemID: navigation.getParam('itemID') }, () => {
// this.state.itemID is now set, you can make your API call.
});
If your only intention to use this.state.itemID is for the URL, I would opt for the first option.

React set state variable

I've an confusion. I'm trying to add array variable in setState. My code is working properly but wanted to confirm some doubt before committing my code.
Which is right way to store array in state variable ?
var names = ['Jake', 'Jon', 'Thruster'];
this.setState({
state: names
});
Or
this.setState((state) => {
state.items.push(names[0]);
return state;
});
What is necessary of return statement here ?
Can some one please explain me the difference here ? I searched in google but I'm still confused.
var names = ['Jake', 'Jon', 'Thruster'];
this.setState({
names //according to Airbnb rules
});
or
this.setState({
names: names
});
this.state.names = ['Jake', 'Jon', 'Thruster'];
setState takes a second argument - callback, that will called after setting State properties
setState({property1: value1, ...}, () => { //some code after State changed })
The first approach is much more common and in my opinion, easier to read. The issue with your code is you don't need to use the key state, because the function you are calling is "setting state". The key should be something like firstNames.
this.setState({
firstNames: names
});
You could also just pass the object in the function like this because setState takes an object as a parameter.
var namesObject = {
firstNames: names
}
this.setState(namesObject);
I would read more about it here and keep doing small tutorials on this an you'll get the hang of it.
https://facebook.github.io/react/docs/react-component.html#setstate

Resources