How do I access the full calendar API so that I can manipulate and use methods like setDates, moveStart etc.. I want to use the methods they provided in this page.
https://fullcalendar.io/docs/Event-setDates
Problem:
The example full calendar provided is class based and is outdated. How do I use these methods in a functional component..
export default class DemoApp extends React.Component {
calendarRef = React.createRef()
render() {
return (
<FullCalendar ref={this.calendarRef} plugins={[ dayGridPlugin ]} />
)
}
someMethod() {
let calendarApi = this.calendarRef.current.getApi()
calendarApi.next()
}
}
What I did so far Currently I use the reference inside handleEventAdd and handleUpdate functions, I take the event and manipulate it accordingly.
<FullCalendar
nowIndicator
plugins={[momentTimezonePlugin, timeGridPlugin, dayGridPlugin, interactionPlugin]}
initialView="timeGridWeek"
headerToolbar={{
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay',
}}
timeZone={userTimezone || Intl.DateTimeFormat().resolvedOptions().timeZone}
ref={calendarRef}
editable
selectable
select={handleEventAdd}
eventResize={(e) => handleupdateSchedule(e)}
/>
What works:
const CalAPI = e.view.calendar
when I do this I can access the Calendar methods like
Calendar::getEvents
Calendar::getEventById
Calendar::addEvent
I use them like this
CalAPI.getEventById(e.event.id).remove() //works
But I cannot access other methods like
Event::setProp
Event::setExtendedProp
Event::setStart
Event::setEnd
Event::setDates
The above don't work. Please find the list of the Event methods I want to use here https://fullcalendar.io/docs/Event-setDates
UPDATE:
Posting more information on the functions.
select={Addevent} // works even when I don`t pass the "e"
eventClick={(e) => Deleteevent(e)} // does not find CalAPI reference
eventDrop={(e) => Updateevent(e)} // does not find CalAPI
eventResize={(e) => Updateevent(e)} // the calendar event refreshes automatically I dont need the CalAPI here.
SOLUTION
Finally after a lot of work, the solution is quite simple actually, we are supposed to use the useRef hook when using functional components. Then access the CalAPI from yourRefname.current.getAPI()... from here on you can access all the needed methods.
PS: Thanks to #ADyson, I got closer to the answer.
Related
I'm currently working at a project with react-three-fiber. I've imported the model with useGLTF from #react-three/drei.
With
const { nodes, materials } = useGLTF('/model.glb');
I access the materials from the glb-file.
To access and manipulate the model I used gltfjsx to generate the model.
Now I need to change the material of a mesh programmatically. Because I have no direct access to the JSX of the model I do it with React.cloneElement and modify the props of the mesh.
So I tried something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial ?
<meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> :
materials['mat_7']
});
If overwriteMaterial is false it works. It shows the material it should. But if it's true then the mesh disappears.
I also thought of putting the <meshStandardMaterial /> in the children prop of the mesh. Something like so:
return React.cloneElement(mesh, {
material: overwriteMaterial ? undefined : materials['mat_7'],
children: overwriteMaterial ? <meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> : undefined
});
With this I always get this error and I don't know why it appears:
TypeError: Cannot read properties of undefined (reading 'visible')
Could this approach somehow work or am I doing something completely wrong?
Every help is welcome. Thanks
Alright so I found the answer by myself after some more hours searching for a solution.
The material property doesn't accept a JSX-tag. So if you create an instance of the class MeshStandardMaterial you can pass it to the property and it works perfectly fine. Now it looks something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial
? new MeshStandardMaterial({ color: 0x0ff000 })
: materials['mat_7']
})
Note: The class MeshStandardMaterial is exported from the three package.
i really don't think you have to clone any react element, that doesn't seem correct. you can clone or mutate materials just as you would in a plain three app. i have no idea why you even want to clone jsx.
const { scene } = useGLTF(url)
const clonedScene = useMemo(() => scene.clone(), [])
useLayoutEffect(() => {
clonedScene.traverse(o => {
if (o.type === 'Mesh') {
o.material = ...
}
})
}, [clonedScene]}
return <primitive object={clonedScene} />
you can skip the clonedScene thing completely as well, this is only if you plan to re-use the model in your scene multiple times.
I have an application developed in Angular with typescript which has 20+ screens with list views of different entities, such as List of Movies, List of Actors, List of theatres etc. I have created a BaseListComponent<T extends Base> in angular and encapsulated most of the common functions such as view, filter, delete, search, pagination etc into this BaseListComponent. And I am creating children of it like MoviesListComponent extends BaseListComponent<Movies>. This way I need to add minimum functionality into children classes which reduces scope of errors and also efforts for unit test cases.
Now I am converting this application into React with typescript. I was studying about react hooks and learnt that many developers are preferring function based approach over classes based components. How can I create above structure with functions. As per my understanding there is no inheritance in functions. So I am doubtful if Generics will be feasible with functions. If not, how can I solve this problem using react's popular approach of decomposition?
Is there any guideline for using Generics or decomposition in react?
To do it "react style", first you have to make a component BaseListComponent. Then put the functions you listed "view, filter, delete, search, pagination etc" in this one. But whenever you want some functions to behave differently for deriving classes, you put a callback in that function.
For example:
renderView = () => { // function of BaseListComponent
const header = (
<div>--I am Header--</div>
);
const footer = () => (
<div>--------</div>
);
const letDerivedClassDecide = this.props.renderCenter();
return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
{header}
{letDerivedClassDecide}
{footer}
</div>
);
}
This example means, you could control the basic layout as a vertical flex div, but you let the deriving class implement the this.props.renderCenter, so that its result can be combined with basic layout.
view = () => { // function of BaseListComponent
let someResult = 100;
someResult = this.props.view();
return someResult;
}
The same idea, this.props.view is implemented by deriving class.
Finally in the deriving class it would be like:
render() { // function of DeriveComponent; can be MoviesListComponent
return (
<BaseListComponent
renderCenter={this.renderCenter}
view={this.view}
>
);
}
Note that, the deriving class is not actually "derive" or extends the base component, it just composite it in the render() function. So both BaseListComponent and MoviesListComponent extends React.Component (or React.PureComponent when considering performance issue).
We're using ant Design in our project and I'm trying to use their Carousel component. The documentation shows methods goto(), next(), prev() but I am unable to access these methods. The way we've done this with other antD components would be to:
const { goto, next, prev } = Carousel;
once you've imported Carousel, but this doesn't work -- those methods are all undefined. These methods were exposed in this 2017 pull request (although the original pull request references previous() instead of prev()) but I see no way to access them. This PR also mentioned that the purpose of the PR was to make the methods available without having to useRefs, which is what all the SO examples seem to show are class-based solutions, not Hooks as we are using.
What am I missing here?
You can create a ref and pass it to the carousel and from that you can access the carousel methods.
const carouselRef = React.createRef();
<Carousel dotPosition={dotPosition} ref={carouselRef}>
<Button onClick={() => {
carouselRef.current.next();
}}
>
goto next
</Button>
<Button
onClick={() => {
carouselRef.current.prev();
}}
>
goto prev
</Button>
</Carousel>
if you want to use the Carousel's methods,you shoule get the instance of Carousel Component.
like this:
codesandbox
I'm attempting to completely recreate or reorganize the functionality of the LayersControl component in its own separate panel using react-leaflet.
I have several filtered into their own and it works fine, but I'd like to customize the look and location of the Control element.
I've hosted the current version of my Leaflet app on github pages here. You can see the control on the right, which is the basic Leaflet control, but I'd like to the Icon on the left (the layers icon) to accomplish the same thing instead with custom react components.
Just wondering if anyone can point me in the right direction to beginning to accomplish this!
This is my current render for my react-leaflet map:
render() {
const types = [...new Set(data.map(loc => loc.type))];
const group = types.map(type =>
data.filter(loc => loc.type === type)
.map(({id, lat, lng, name}) =>
<LayersControl.Overlay name={startCase(toLower(type))}>
<LayerGroup>
<Marker key={id} position={[lat, lng]} icon=
{locationIcon}>
<Tooltip permanent direction="bottom" opacity={.6}>
{name}
</Tooltip>
</Marker>
</LayerGroup>
</LayersControl.Overlay>
));
return (
<>
<ControlPanel />
<Map
zoomControl={false}
center={this.state.center}
zoom={this.state.zoom}
maxBounds={this.state.maxBounds}
maxZoom={10}
>
<LayersControl>
<TileLayer
url='https://cartocdn-gusc.global.ssl.fastly.net//ramirocartodb/api/v1/map/named/tpl_756aec63_3adb_48b6_9d14_331c6cbc47cf/all/{z}/{x}/{y}.png'
/>
<ZoomControl position="topright" />
{group}
</LayersControl>
</Map>
</>
);
}
So theres still a few bugs in this but i've managed get most of the way (self taught react) using material UI as an example, can be seen in this sandbox link:
https://codesandbox.io/embed/competent-edison-wt5pl?fontsize=14
The general bassis is that we extend MapControl which means we have to define createLeafletElement, this has to return a generic leaflet (not react) control from the original javascript leaflet package. Essentially making a div with the domutil provided by leaflet and then portaling our react components through that div with react portals.
Again with another class extension we extend some of the classes provided by react-leaflet for layers, i pulled it out and just made a generic layer that you could define a group for, that way you could render any layer (polygon, baselayer etc) and specify the group to tell it where to go in the layer control i.e no need for specific components or overlays. As we are extending the class we need implement and pass down the methods we want to use, like addLayer, remove layer etc. During these implementations i've just added them to state to track what layers are active and such.
Not sure if there are better practices throughout everything i've implemented but this is definitely a start, hopefully in the right direction.
Bugs - The first layer in each group won't turn on correctly without the 2nd item ticked, something to do with state i think but didn't have the time to track it down
Thanks Dylan and Peter for this nice React Leaflet custom control approach. I assumed there was still a bug in the toggleLayer function. It's checked multiple checkboxes and the layers won't change properly. So I restructered a little bit and now it should work fine.
toggleLayer = layerInput => {
const { name, group } = layerInput;
let layers = { ...this.state.layers };
layers[group] = layers[group].map(l => {
l.checked = false;
this.removeLayer(l.layer);
if (l.name === name) {
l.checked = !l.checked;
this.props.leaflet.map.addLayer(l.layer);
}
return l;
});
this.setState({
layers
});
};
Just to elaborate on the bug that is mentioned in Dylans answer...
If you have more then one ControlledLayerItem, none items are added to the map until the very last item is checked. To fix this, the toggleLayer method in ControlLayer2.js has to be slightly modified:
toggleLayer = layerInput => {
const { layer, name, checked, group } = layerInput;
let layers = { ...this.state.layers };
layers[group] = layers[group].map(l => {
if (l.name === name) {
l.checked = !l.checked;
l.checked
? this.props.leaflet.map.addLayer(layer)
: this.removeLayer(layer);
}
return l;
});
this.setState({
layers
});
};
Thanks Dylan for the code, it was really helpfull.
I'm trying to implement a simple Onsen Navigator in React.
So far I'm receiving an error 'route is not defined' and I was looking through the examples & docs but I only saw the initialRoute prop was provided, how & where does the route prop generated or something? Cause it seems like its not specified.
Here is my the code of my component:
import React, {PropTypes} from 'react';
import ons from 'onsenui';
import * as Ons from 'react-onsenui';
class SignUp extends React.Component {
constructor(props) {
super(props);
this.state = {
index : 0
};
this.renderPage = this.renderPage.bind(this);
this.pushPage = this.pushPage.bind(this);
}
pushPage(navigator) {
navigator.pushPage({
title: `Another page ${this.state.index}`,
hasBackButton: true
});
this.setState({index: this.state.index++});
}
renderPage(route, navigator) {
return (
<Ons.Page key={route.title}>
<section style={{margin: '16px', textAlign: 'center'}}>
<Ons.Button onClick={this.pushPage}>
Push Page
</Ons.Button>
</section>
</Ons.Page>
);
}
render() {
return (
<Ons.Page key={route.title}>
<Ons.Navigator
renderPage={this.renderPage}
initialRoute={{
title: 'First page',
hasBackButton: false
}}
/>
</Ons.Page>
);
}
};
SignUp.propTypes = {
'data-pageName': PropTypes.string.isRequired
};
export default SignUp;
Is this the right syntax in ES6? Have I missed something?
When using Ons.Navigator in react the two required properties are:
initialRoute - it should be an object.
renderPage - method which receives 2 arguments - route and navigator. The route should be an object similar to the initialRoute one. You provide that object when you are calling pushPage and similar methods.
It seems that you already know these 2, but there still 2 other things which you need to be careful about. They are not directly onsen related, but come up a lot when using react in general.
Whenever you have a list of dom elements (for example an array of Ons.Page tags) each of those should have a unique key property.
Whenever you use a method you need to make sure you are binding it if you need some extra arguments.
It seems you also know these two. So the only thing left is to make sure you follow them.
Your syntax is correct - the only thing missing is the route variable in SignUp.render. Maybe you originally copied the renderPage method and that is how you have a leftover Ons.Page.
If you're not putting the SignUp component inside some other navigator, tabbar or splitter then you don't actually need the Ons.Page in its render method. Those are the only cases when they are needed. If you it happens to have one of those components as a parent then you can just specify the key.
PS: I think there should be a React Component Inspector (something like this) which you can install - then I think you may be able to see the place where the error occurs. I think if you knew on which line the problem was you would have been able to solve it. :)
For me, with the object I was passing to initialRoute(), it needed a props property, which itself was an object with a key property. See the before and after below.
Before fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage}}
renderPage={this.renderPage}
/>
);
}
}
This was causing the following console warning:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `Navigator`.
After fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage, props: {key: 'DataEntryPage'}}}
renderPage={this.renderPage}
/>
);
}
}
Notice that the difference I needed to make was the addition of , props: {key: 'DataEntryPage'}.
Feel free to check out this medium article for more information.