How to use the rerenderEvents() method on a pre-rendered Calendar - reactjs

UPDATED 3-26-2019
I am trying to give users the ability to add events to the Calendar and for those events to appear upon input. I am using a React.js front end and can get the events to render in a new calendar but it does not delete the old one (I just end up with 10+ calenders that render on top of each other).
I've tried using the .destroy() method and then re-rendering the calendar but the method doesn't seem to be available/functioning so I figured I would try rerenderEvents() and keep the same calendar object but that function doesn't seem to be accessible either. Would love some assistance in solving this from anyone who is familiar with FullCalendar v.4
Once the user inputs the data it's collected into a data object and append it into the state but if I just mirror the above, it renders a whole new calendar on top of the old one.
It's like I cannot get the Calendar object once rendered to make those method calls, but am having trouble capturing it in a separate function. All the Docs say is call the .destroy() or call the .rerenderEvents() and not what to call them on.
//This successfully Renders
componentOnMount() {
var calendarNew = document.getElementById('calendar');
let newCalendar = new Calendar(calendarNew, {
plugins: [ dayGridPlugin, timeGridPlugin, listPlugin ],
events: this.state.events
});
console.log(newCalendar)
await this.setState({ calendarObj: newCalendar })
// let myNewEvents = newCalendar.getEvents()
// console.log(myNewEvents)
// let stateEvents = this.state.calendarObj.getEvents()
// console.log(stateEvents)
this.state.calendarObj.render();
}
I've tried the following to mitigate the double render...
async handleNewEvent() {
// code that creates an object and sets the state to the new event array//
var newEventArr = existingEvents.concat(newEvent)
console.log(newEventArr)
await this.setState({ events: newEventArr })
await this.setState({ currentIndex: (this.state.currentIndex + 1) })
this.props.change(this.state.events, this.state.currentIndex)
//Contains the new event array in the console
console.log(this.state.events)
this.state.calendarObj.rerenderEvents();
}
But this does nothing. I cannot even render() from the handleNewEvent() as it appears as if the state is unable to hold functions in it and I cannot find a way to pass the function from the ComponentDidMount() to the handleNewEvent(). Im at a loss...

Related

GraphQL Automatic refetch on empty responses

I want to randomize movies from theMovieDB API. First I send a request to access the ID of the latest entry:
const { loading: loadingLatest, error: errorLatest, data: latestData, refetch: refetchLatest } = useQuery(
LATEST_MOVIE_QUERY
);
Then I want to fetch data from a randomly selected ID between 1 and the number of the latest id. Using a variable dependant on the first query seems to break the app, so for now I'm just using the same movie every time upon mounting the component:
const [
movieState,
setMovieState
] = useState(120);
const { loading, error, data, refetch } = useQuery(ONE_MOVIE_BY_ID_QUERY, {
variables : { movieId: movieState },
skip : !latestData
});
I want to press a button to fetch a new random movie, but the problem is that many of the IDs in the API lead to deleted entries and then I get an error back. I want to keep refetching until I get a good response back but I have no idea to implement it. Right now my randomize function just looks like this:
const randomizeClick = () => {
let mostRecentID = latestData.latestMovie.id;
setMovieState(Math.floor(Math.random() * mostRecentID));
};
I'd be grateful if someone can help me how to implement this.
I think what you needs is the "useLazyQuery" functionality of Apollo. You can find more information about it here: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
With useLazyQuery you can change your variables easily and this is meant to be fired after a certain event (click or something similar). The useQuery functionality will be loaded when the component is mounted.

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.

reactjs -- handle checkbox (multiple) click events

I know there are some similar topics but none seems to be in the same direction of what I'm trying to do, thus a new thread.
I have a component that displays a list of keys, each with a checkbox attached to the string. In addition, I have a button that supposedly calls an API with all keys selected and delete these keys.
Several things I'm trying to achieve:
checking a check box enables the delete button
click the delete button should send a POST to API, the list should then reload
Since the list is reloaded, all checkbox should be unselected, thus the delete button is once again disabled
there's another button outside of this function that checks for the length of the list as well, which I don't know how to associate with this list if I fetch the list in the component.
I'm facing the problem which I don't know how to make the button and the checkboxes associate to each other. I tried using state with a checked state, which is a boolean, but that's only one boolean and cannot record several keys. I think using an array would work? Then again I'm not sure how to properly append or remove the key checked.
my code looks like
class AppList extends Component {
constructor() {
super();
this.state = {
checked: [],
apps: []
};
this.handleChecked = this.handleChecked.bind(this);
}
componentDidMount() {
fetch("some_url", {
method: 'post',
body: JSON.stringify({"user": "some_email"}),
headers: {'Content-Type': ' application/json'}
})
.then(res => res.json())
.then(
(result) => {
this.setState({apps: JSON.parse(result)});
},
(error) => {
console.log("error", error);
}
);
}
handleDeleteKey = (event) => {
// fetch(I'll worry about this later)
this.setState({checked: false});
console.log("delete!!!!");
}
handleChecked () {
this.setState({checked: !this.state.checked});
}
render () {
const apps = this.state.apps.map((app) =>
<div>
<input type="checkbox" onChange={this.handleChecked} />
{` ${app}`}
</div>
);
return (
<div>
<h4>Client Key List:</h4>
{this.state.apps.length > 0 ? <ul>{apps}</ul> : <p>No Key</p>}
{this.state.apps.length > 0 ? <button className="api-instruction-button" onClick={this.handleDeleteKey}>Delete Selected Key(s)</button> : null}
</div>
);
}
}
export default AppList;
I feel like my design is completely wrong but I don't know how to fix it. It seems like there are so many states to be passed around and nothing is the outermost, almost a cyclic dependency.
Anyone had any experience dealing with this problem? It seems like it's a common user action but I can't figure it out.
EDIT: after digging it a bit more, it seems like I need to call componentDidMount outside of the AppList. It should be in the component that uses AppList, let's call it MainApp.
MainApp calls componentDidMount which is the same as the one in AppList. The one in AppList gets removed, and the keys are passed to AppList as props.
I have trouble handling the clicking event. It seems like the component is always updating, so if I want to append the clicked key to the array, it wouldn't work. The same call will be made again and again.
Since there's another button in MainApp that requires the list of keys, I can't just pass the call into AppList. However, updating in AppList should update the MainApp as well. How does it work? I'm so confused
EDIT2:
https://codesandbox.io/s/7w2w11477j
This recreation should contain all functions I have so far, but I can't get them to work together.
Again my task is simply:
I have a list of strings, each with a checkbox
checking the checkbox selects the specific string
There's a button that I can click to delete these entries in my db by calling an API
Is refreshing the MainApp needed in this case? Otherwise I need to delete the strings in frontend so they don't display after the delete button is pressed
Here's what I believe you were going for: https://codesandbox.io/s/w23wv002yw
The only problem that made yours not work properly was you were just getting a little jumbled with where to put everything.
Contents:
The MainApp.js will only contain the apps and a method for deleting them in the backend. Other than those two methods, nothing else really concerns the MainApp.js file.
The AppList.js will contain all the methods that update its own checked state, the delete button itself, and a method to clear the checked state on delete.
Processes:
First, MainApp.js will load and remount with a backend api pull and populate its apps state. Once it's finished that, it will pass it on to AppList.js. From there, AppList.js will render that list as a multi-select field onscreen. The user can then select or deselect any of the options. As an option is selected, its index is pushed to the checked state and organized in ascending order.
(ordering the array isn't that necessary, but I figured it would help if you wanted to retool it sometime down the road)
When one or more option is selected, a delete button will appear. When the user clicks the delete button, AppList.js will call the delete function passed to it from MainApp.js, then it will clear the current checked state.

React with Redux Update only state

I'm working on integrating Redux in an already finished SPA with ReactJS.
On my HomePage I have a list of the 4 newest collections added which on render, I fetch with axios from my database. These are then saved in Redux Store and displayed on the React UI.
My mapStateToProps look something like this:
const mapStateToProps = (state) => ({
credentials: credentials(state),
collections: collections(state)
});
Where credentials is irrelevant and collections is:
const collections = (state) => {
if (state.collectionsHomeViewReducer.fetching === true) {
return {
fetchingCollections: true
}
}
else if (state.collectionsHomeViewReducer.data) {
const response = state.collectionsHomeViewReducer.data;
return {
collections: response.collections,
fetchedCollections: true,
fetchingCollections: false
}
}
else if (state.collectionsHomeViewReducer.fetched === false) {
return {
fetchedCollections: false,
fetchingCollections: false
}
}
};
What is it I want to do:
Update the store state every time another client, or the current client, adds a new collection. Moreover, I do not wish for the UI to update immediately after I dispatch(action), I want it to update when a user refreshes the page or when he navigates to another view and returns ( I believe what I'm trying to say is when componentDidMount is called ).
What have I achieved so far:
By using socket.io, I
socket.emit("updateCollectionsStore")
socket.on("updateCollectionsStore")
and
socket.broadcast.emit("updateCollectionsStore")
in their respective places in the application. The final call of
socket.on("updateCollectionsStore")
after the broadcast, is in the main file of the page, app.jsx where the store is also located. The function there looks like this:
socket.on("updateCollectionsStore", () => {
store.dispatch(getCollectionsHomeView());
});
The store is updated and everything works fine, as viewed from the Redux Dev Tools.
What I can't seem to figure out is to tell the props not to change due to the fact that mapStateToProps is called every time an action is dispatched.
Why do I need this: The HomePage can deal with a continuous UI update and data fetching but I also have a page ReadAllPage where you can real all collections. The problem is if there will always be the newest post on the top, whenever a new one is added, the current one is pushed downwards. In case somebody had the intent to click the one that was pushed down, now he might have accidentally clicked the one that took its place, which is not wanted.
What else should I do different or further to achieve the result I want?
Thank you.
According to your needs, I would have two properties in the state. First is that is currently visible on the HomeView and the second is that is updated via sockets. Once a user navigates to the HomeView you can just replace the first collection with the second one.

Update value of variable from Firebase query?

I currently am trying to pull the value of an item from a firebase using once, and use this value to populate the var itemsList. However, although itemsList is correctly populated within the once call, it is logging as undefined outside the call. I think this may have something to do with the asynchronous nature of Firebase, but I'm not sure how to remedy this problem. Any suggestions?
submitAnswer: function() {
var userRef = usersRef.child(key);
//get items_list for user
var itemsList;
userRef.once('value', (snap) => {
itemsList = snap.val().items_list;
console.log('items list populated here' + itemsList);
});
console.log("items list not populated here" + itemsList);
},
You're correct about the Firebase call, it's asynchronous and the code isn't going to wait for it to complete before logging the unpopulated itemsList.
If you're only looking to do something simple with the data, just be sure to check that it exists before performing any action with it (and handle it like you would any async data).
if(itemsList){
console.log('My items have arrived! ' + itemsList);
}
If that data is going to be propagated further down your app it is usually suggested to make a call to setState() with your response data from Firebase to trigger a re-render of your components with the new data you just fetched.
So something along the lines of:
userRef.once("value", (snap) => {
itemsList = snap.val().items_list;
this.setState({
items: itemsList;
});
}.bind(this));

Resources