Resize feature seems to be incompatible with external drag-and-drop - react-dnd

First of all, I would like to express my gratitude for this repo: https://github.com/Tim1023/react-scheduler-firebase without which I would not be able to implement external drag-and-drop for react-big-calendar.
I have spent an entire week trying to play with this stuff, but resize feature (stretching the event up and down) just won't work for it. I guess the problem is with css ?
I have tried to upgrade the react-big-calendar's version from 0.19 to 0.20, and situation is the opposite - resize works perfectly, but external DnD no longer works (namely, when dragging an external object on the calendar, drop mode does not get activated)
There's an open issue regarding this, but looks like it remains unhandled up to now.

The latest version of RBC does include external drag and drop. Although the documentation site has not yet been updated, cloning the repo and running the 'examples' will show a working demo of this functionality. Here is the source for that demo:
import React from 'react'
import events from '../events'
import { Calendar, Views } from 'react-big-calendar'
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop'
import Layout from 'react-tackle-box/Layout'
import Card from '../Card'
import 'react-big-calendar/lib/addons/dragAndDrop/styles.scss'
const DragAndDropCalendar = withDragAndDrop(Calendar)
const formatName = (name, count) => `${name} ID ${count}`
class Dnd extends React.Component {
constructor(props) {
super(props)
this.state = {
events: events,
draggedEvent: null,
counters: {
item1: 0,
item2: 0,
},
displayDragItemInCell: true,
}
}
handleDragStart = event => {
this.setState({ draggedEvent: event })
}
handleDisplayDragItemInCell = () => {
this.setState({
displayDragItemInCell: !this.state.displayDragItemInCell,
})
}
dragFromOutsideItem = () => {
return this.state.draggedEvent
}
customOnDragOver = event => {
// check for undroppable is specific to this example
// and not part of API. This just demonstrates that
// onDragOver can optionally be passed to conditionally
// allow draggable items to be dropped on cal, based on
// whether event.preventDefault is called
if (this.state.draggedEvent !== 'undroppable') {
console.log('preventDefault')
event.preventDefault()
}
}
onDropFromOutside = ({ start, end, allDay }) => {
const { draggedEvent, counters } = this.state
const event = {
title: formatName(draggedEvent.name, counters[draggedEvent.name]),
start,
end,
isAllDay: allDay,
}
const updatedCounters = {
...counters,
[draggedEvent.name]: counters[draggedEvent.name] + 1,
}
this.setState({ draggedEvent: null, counters: updatedCounters })
this.newEvent(event)
}
moveEvent = ({ event, start, end, isAllDay: droppedOnAllDaySlot }) => {
const { events } = this.state
const idx = events.indexOf(event)
let allDay = event.allDay
if (!event.allDay && droppedOnAllDaySlot) {
allDay = true
} else if (event.allDay && !droppedOnAllDaySlot) {
allDay = false
}
const updatedEvent = { ...event, start, end, allDay }
const nextEvents = [...events]
nextEvents.splice(idx, 1, updatedEvent)
this.setState({
events: nextEvents,
})
// alert(`${event.title} was dropped onto ${updatedEvent.start}`)
}
resizeEvent = ({ event, start, end }) => {
const { events } = this.state
const nextEvents = events.map(existingEvent => {
return existingEvent.id == event.id
? { ...existingEvent, start, end }
: existingEvent
})
this.setState({
events: nextEvents,
})
//alert(`${event.title} was resized to ${start}-${end}`)
}
newEvent = event => {
let idList = this.state.events.map(a => a.id)
let newId = Math.max(...idList) + 1
let hour = {
id: newId,
title: event.title,
allDay: event.isAllDay,
start: event.start,
end: event.end,
}
this.setState({
events: this.state.events.concat([hour]),
})
}
render() {
return (
<div>
<Card className="examples--header" style={{ display: 'flex' }}>
<div
style={{
display: 'flex',
flex: 1,
justifyContent: 'center',
flexWrap: 'wrap',
}}
>
<h4 style={{ color: 'gray', width: '100%' }}>
Outside Drag Sources
</h4>
{Object.entries(this.state.counters).map(([name, count]) => (
<div
style={{
border: '2px solid gray',
borderRadius: '4px',
width: '100px',
margin: '10px',
}}
draggable="true"
key={name}
onDragStart={() =>
this.handleDragStart({ title: formatName(name, count), name })
}
>
{formatName(name, count)}
</div>
))}
<div
style={{
border: '2px solid gray',
borderRadius: '4px',
width: '100px',
margin: '10px',
}}
draggable="true"
key={name}
onDragStart={() => this.handleDragStart('undroppable')}
>
Draggable but not for calendar.
</div>
</div>
<div>
<label>
<input
style={{ marginRight: 5 }}
type="checkbox"
checked={this.state.displayDragItemInCell}
onChange={this.handleDisplayDragItemInCell}
/>
Display dragged item in cell while dragging over
</label>
</div>
</Card>
<DragAndDropCalendar
selectable
localizer={this.props.localizer}
events={this.state.events}
onEventDrop={this.moveEvent}
dragFromOutsideItem={
this.state.displayDragItemInCell ? this.dragFromOutsideItem : null
}
onDropFromOutside={this.onDropFromOutside}
onDragOver={this.customOnDragOver}
resizable
onEventResize={this.resizeEvent}
onSelectSlot={this.newEvent}
onD
defaultView={Views.MONTH}
defaultDate={new Date(2015, 3, 12)}
/>
</div>
)
}
}
export default Dnd

Related

Emotion.js UI Component Library, css Prop pass problem

I building a UI Component Library based on Emotion.js.
ex) Skeleton Component,
// package name is "my-library"
import { HTMLAttributes } from 'react'
import { keyframes } from '#emotion/react'
interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
variant?: 'circle' | 'rectangle'
}
const pulse = keyframes`
50% {
opacity: .5;
}
`
export const Skeleton = ({ variant = 'rectangle', ...rest }: SkeletonProps) => {
return (
<div
css={{
borderRadius: variant === 'rectangle' ? 6 : '50%',
backgroundColor: 'rgb(203,213,225)',
animation: `${pulse} 2s cubic-bezier(.4,0,.6,1) infinite`,
}}
{...rest}
/>
)
}
usage)
import { Skeleton } from 'my-library'
const Test = () => {
return (
<Skeleton css={{ backgroundColor: 'pink' }} />
)
}
I used “css Props” to pass styles
contrary to one's expectation,
the styles are not merged and the “class” is split in two
Screenshot Capture

In Agora GroupVoice call mouse click event not working after user joining as host or audience

I am trying to build voice chat using creatstream and mode live in nextjs project. But experiencing some unsual behaviour. After the call started if there is more than one host or someone leave and join , loosing the mouse click event on the screen. Which means cannot host/audience can’t mute/unmute or leave the call. But can do the same with the keyboard controls. Could someone please explain, what is that i am missing. I am attaching the code of agora for host. It will be great if someone can help me to understand the problem. We are also generating RTCtoken. Thanks in advance!
function AgoraVoiceCall({ channel, attendeeMode, appId, uid, user }) {
console.log(attendeeMode);
const [client, setClient] = useState(null);
const [localStream, setLocalStream] = useState(null);
//Mutation
const [generateAgoraToken, { data, loading }] = useMutation(
GENERATE_SPACE_TOKEN
);
var options = {
token: undefined,
uid: Math.floor(Math.random() * Date.now() * 0.001).toString(),
};
const join = async () => {
let clientInstance = Agora.createClient({ mode: 'live', codec: 'vp8' });
clientInstance.setClientRole(attendeeMode);
const token = await generateAgoraToken({
variables: { channelName: channel, uid: options.uid, role: attendeeMode },
});
(options.token = token && token.data.generateAgoraToken),
console.log('token', token.data.generateAgoraToken);
clientInstance.init(appId, () => {
clientInstance.join(options.token, channel, options.uid, uid => {
let localStreamInstance = Agora.createStream({
streamID: uid,
audio: true,
video: false,
screen: false,
});
setLocalStream(localStreamInstance);
localStreamInstance.init(() => {
clientInstance.publish(localStreamInstance);
const div = document
.getElementById('local_stream')
.insertAdjacentHTML(
'afterBegin',
`<div id="player-wrapper-${options.uid}">
<p class="player-name">LocalUser(${options.uid})</p>
</div>`
);
localStreamInstance.play('local_stream');
});
clientInstance.on('stream-added', evt => {
let remoteStream = evt.stream;
const id = remoteStream.getId();
const div = document
.getElementById('remote_stream')
.insertAdjacentHTML(
'afterBegin',
`<div id="player-wrapper-${id}">
<p class="player-name">RemoteUser(${id})</p>
</div>`
);
console.log(div);
clientInstance &&
clientInstance.subscribe(remoteStream, function(err) {
console.log('Subscribe stream failed', err);
});
});
clientInstance.on('stream-subscribed', evt => {
let remoteStream = evt.stream;
remoteStream.play('remote_stream');
});
clientInstance.on('stream-unpublished', evt => {
console.log('peer-leave', evt.stream);
let remoteStream = evt.stream;
const id = remoteStream.getId();
delete remoteUsers[id];
const remoteUserContainer = document.getElementById(
`player-wrapper-${id}`
);
remoteUserContainer.remove();
console.log(remoteUsers);
setRemoteUsersData(remoteUsers);
});
});
});
setClient(clientInstance);
};
const leaveCall = () => {
// Destroy the local audio and track.
client && client.unpublish(localStream);
localStream && localStream.close();
const id = localStream.getId();
const localStreamContainer = document.getElementById(
`player-wrapper-${id}`
);
localStreamContainer && localStreamContainer.remove();
// Leave the channel.
client &&
client.leave(
() => {
console.log('Client succeed to leave.');
},
() => {
console.log('Client failed to leave.');
}
);
setLocalStream(null);
};
const handleMic = () => {
const btn = document.getElementById('mic-btn');
if (localStream.isAudioOn()) {
localStream.muteAudio();
btn.innerHTML = 'UNMUTE';
} else {
localStream.unmuteAudio();
btn.innerHTML = 'MUTE';
}
};
return (
<Wrapper>
<div
style={{
display: 'flex',
flexDirection: 'column',
margin: '0 auto',
height: '100vh',
width: '60vw',
backgroundColor: 'lightblue',
}}
>
<h2>Welcome to Neospace Voice Call</h2>
<div
style={{
height: '30vh',
backgroundColor: 'pink',
border: '3px solid black',
}}
id="local_stream"
>
<Avatar src={user.avatarUrl ?? '/avatar_placeholder.svg'} />
</div>
<div
style={{
height: '30vh',
backgroundColor: 'yellow',
border: '3px solid black',
}}
id="remote_stream"
>
<Avatar src={user.avatarUrl ?? '/avatar_placeholder.svg'} />
</div>
<div
style={{
height: '20vh',
backgroundColor: 'orange',
border: '3px solid black',
}}
>
control
<div
style={{
margin: '5vh 0 0 30vh',
}}
>
{/* All these button onclick with the mouse is not working */}
<button
onClick={join}
style={{ marginRight: '15px', fontSize: '20px' }}
disabled={localStream}
>
JOIN CALL
</button>
<button
id="mic-btn"
onClick={e => {
handleMic(e);
}}
style={{ marginRight: '15px', fontSize: '20px' }}
>
MUTE
</button>
<button onClick={leaveCall} style={{ fontSize: '20px' }}>
END CALL
</button>
</div>
</div>
</div>
</Wrapper>
);
}
export default withApollo(AgoraVoiceCall);
From your explanation it sounds like a UI problem, I'd just logging out the click event to see if it's a problem with the UI.

The best way of tracking location in background using react-native + Expo in 2020

I want to create my own Endomono/Runtastic-like app using RN + expo (This app will be just for me, and I have android phone with pretty decent performance/battery life (Redmi note 7) so I don't worry about performance too much). I wanted to use all-in-one library for that, or just and library that allows me to execute some code each X seconds in background (and getAsyncLocation there). My point is just to send lat/lon data every X seconds to my backend HTTP django-rest-framework powered server.
I just spent whole day trying figure out any way to do that, I tried couple of libraries like this ones: react-native-background-geolocation, react-native-background-timer, react-native-background-job and few more. I followed step by step instalation guide, and I kept getting errors like: null is not an object (evaluating 'RNBackgroundTimer.setTimeout') .
I also tried this: I fixed some errors in this code (imports related), it seemed to work, but when I changed my GPS location using Fake GPS, and only one cast of didFocus functions appears in the console. Here's code:
import React from 'react';
import { EventEmitter } from 'fbemitter';
import { NavigationEvents } from 'react-navigation';
import { AppState, AsyncStorage, Platform, StyleSheet, Text, View, Button } from 'react-native';
import MapView from 'react-native-maps';
import * as Permissions from 'expo-permissions';
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';
import { FontAwesome, MaterialIcons } from '#expo/vector-icons';
const STORAGE_KEY = 'expo-home-locations';
const LOCATION_UPDATES_TASK = 'location-updates';
const locationEventsEmitter = new EventEmitter();
export default class MapScreen extends React.Component {
static navigationOptions = {
title: 'Background location',
};
mapViewRef = React.createRef();
state = {
accuracy: 4,
isTracking: false,
showsBackgroundLocationIndicator: false,
savedLocations: [],
initialRegion: null,
error: null,
};
didFocus = async () => {
console.log("Hello")
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
AppState.addEventListener('change', this.handleAppStateChange);
this.setState({
error:
'Location permissions are required in order to use this feature. You can manually enable them at any time in the "Location Services" section of the Settings app.',
});
return;
} else {
this.setState({ error: null });
}
const { coords } = await Location.getCurrentPositionAsync();
console.log(coords)
const isTracking = await Location.hasStartedLocationUpdatesAsync(LOCATION_UPDATES_TASK);
const task = (await TaskManager.getRegisteredTasksAsync()).find(
({ taskName }) => taskName === LOCATION_UPDATES_TASK
);
const savedLocations = await getSavedLocations();
const accuracy = (task && task.options.accuracy) || this.state.accuracy;
this.eventSubscription = locationEventsEmitter.addListener('update', locations => {
this.setState({ savedLocations: locations });
});
if (!isTracking) {
alert('Click `Start tracking` to start getting location updates.');
}
this.setState({
accuracy,
isTracking,
savedLocations,
initialRegion: {
latitude: coords.latitude,
longitude: coords.longitude,
latitudeDelta: 0.004,
longitudeDelta: 0.002,
},
});
};
handleAppStateChange = nextAppState => {
if (nextAppState !== 'active') {
return;
}
if (this.state.initialRegion) {
AppState.removeEventListener('change', this.handleAppStateChange);
return;
}
this.didFocus();
};
componentWillUnmount() {
if (this.eventSubscription) {
this.eventSubscription.remove();
}
AppState.removeEventListener('change', this.handleAppStateChange);
}
async startLocationUpdates(accuracy = this.state.accuracy) {
await Location.startLocationUpdatesAsync(LOCATION_UPDATES_TASK, {
accuracy,
showsBackgroundLocationIndicator: this.state.showsBackgroundLocationIndicator,
});
if (!this.state.isTracking) {
alert(
'Now you can send app to the background, go somewhere and come back here! You can even terminate the app and it will be woken up when the new significant location change comes out.'
);
}
this.setState({ isTracking: true });
}
async stopLocationUpdates() {
await Location.stopLocationUpdatesAsync(LOCATION_UPDATES_TASK);
this.setState({ isTracking: false });
}
clearLocations = async () => {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify([]));
this.setState({ savedLocations: [] });
};
toggleTracking = async () => {
await AsyncStorage.removeItem(STORAGE_KEY);
if (this.state.isTracking) {
await this.stopLocationUpdates();
} else {
await this.startLocationUpdates();
}
this.setState({ savedLocations: [] });
};
onAccuracyChange = () => {
const next = Location.Accuracy[this.state.accuracy + 1];
const accuracy = next ? Location.Accuracy[next] : Location.Accuracy.Lowest;
this.setState({ accuracy });
if (this.state.isTracking) {
// Restart background task with the new accuracy.
this.startLocationUpdates(accuracy);
}
};
toggleLocationIndicator = async () => {
const showsBackgroundLocationIndicator = !this.state.showsBackgroundLocationIndicator;
this.setState({ showsBackgroundLocationIndicator }, async () => {
if (this.state.isTracking) {
await this.startLocationUpdates();
}
});
};
onCenterMap = async () => {
const { coords } = await Location.getCurrentPositionAsync();
const mapView = this.mapViewRef.current;
if (mapView) {
mapView.animateToRegion({
latitude: coords.latitude,
longitude: coords.longitude,
latitudeDelta: 0.004,
longitudeDelta: 0.002,
});
}
};
renderPolyline() {
const { savedLocations } = this.state;
if (savedLocations.length === 0) {
return null;
}
return (
<MapView.Polyline
coordinates={savedLocations}
strokeWidth={3}
strokeColor={"black"}
/>
);
}
render() {
if (this.state.error) {
return <Text style={styles.errorText}>{this.state.error}</Text>;
}
if (!this.state.initialRegion) {
return <NavigationEvents onDidFocus={this.didFocus} />;
}
return (
<View style={styles.screen}>
<MapView
ref={this.mapViewRef}
style={styles.mapView}
initialRegion={this.state.initialRegion}
showsUserLocation>
{this.renderPolyline()}
</MapView>
<View style={styles.buttons} pointerEvents="box-none">
<View style={styles.topButtons}>
<View style={styles.buttonsColumn}>
{Platform.OS === 'android' ? null : (
<Button style={styles.button} onPress={this.toggleLocationIndicator} title="background/indicator">
<Text>{this.state.showsBackgroundLocationIndicator ? 'Hide' : 'Show'}</Text>
<Text> background </Text>
<FontAwesome name="location-arrow" size={20} color="white" />
<Text> indicator</Text>
</Button>
)}
</View>
<View style={styles.buttonsColumn}>
<Button style={styles.button} onPress={this.onCenterMap} title="my location">
<MaterialIcons name="my-location" size={20} color="white" />
</Button>
</View>
</View>
<View style={styles.bottomButtons}>
<Button style={styles.button} onPress={this.clearLocations} title="clear locations">
Clear locations
</Button>
<Button style={styles.button} onPress={this.toggleTracking} title="start-stop tracking">
{this.state.isTracking ? 'Stop tracking' : 'Start tracking'}
</Button>
</View>
</View>
</View>
);
}
}
async function getSavedLocations() {
try {
const item = await AsyncStorage.getItem(STORAGE_KEY);
return item ? JSON.parse(item) : [];
} catch (e) {
return [];
}
}
if (Platform.OS !== 'android') {
TaskManager.defineTask(LOCATION_UPDATES_TASK, async ({ data: { locations } }) => {
if (locations && locations.length > 0) {
const savedLocations = await getSavedLocations();
const newLocations = locations.map(({ coords }) => ({
latitude: coords.latitude,
longitude: coords.longitude,
}));
savedLocations.push(...newLocations);
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(savedLocations));
locationEventsEmitter.emit('update', savedLocations);
}
});
}
const styles = StyleSheet.create({
screen: {
flex: 1,
},
mapView: {
flex: 1,
},
buttons: {
flex: 1,
flexDirection: 'column',
justifyContent: 'space-between',
padding: 10,
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
},
topButtons: {
flexDirection: 'row',
justifyContent: 'space-between',
},
bottomButtons: {
flexDirection: 'column',
alignItems: 'flex-end',
},
buttonsColumn: {
flexDirection: 'column',
alignItems: 'flex-start',
},
button: {
paddingVertical: 5,
paddingHorizontal: 10,
marginVertical: 5,
},
errorText: {
fontSize: 15,
color: 'rgba(0,0,0,0.7)',
margin: 20,
},
});
If you know any way to easily complete my target (of sending simple HTTP GET with location from background of Expo + RN app to my DRF backend) please let me know.
If you're using Expo you can simply use expo-task-manager and expo-location to get background location updates.
Here's a simplified version that I'm using (and it's working for sure on Android) on the App I'm currently developing:
import * as Location from 'expo-location';
import * as TaskManager from 'expo-task-manager';
import axios from 'axios';
const TASK_FETCH_LOCATION = 'TASK_FETCH_LOCATION';
// 1 define the task passing its name and a callback that will be called whenever the location changes
TaskManager.defineTask(TASK_FETCH_LOCATION, async ({ data: { locations }, error }) => {
if (error) {
console.error(error);
return;
}
const [location] = locations;
try {
const url = `https://<your-api-endpoint>`;
await axios.post(url, { location }); // you should use post instead of get to persist data on the backend
} catch (err) {
console.error(err);
}
});
// 2 start the task
Location.startLocationUpdatesAsync(TASK_FETCH_LOCATION, {
accuracy: Location.Accuracy.Highest,
distanceInterval: 1, // minimum change (in meters) betweens updates
deferredUpdatesInterval: 1000, // minimum interval (in milliseconds) between updates
// foregroundService is how you get the task to be updated as often as would be if the app was open
foregroundService: {
notificationTitle: 'Using your location',
notificationBody: 'To turn off, go back to the app and switch something off.',
},
});
// 3 when you're done, stop it
Location.hasStartedLocationUpdatesAsync(TASK_FETCH_LOCATION).then((value) => {
if (value) {
Location.stopLocationUpdatesAsync(TASK_FETCH_LOCATION);
}
});
It doesn't necessarily work with Expo, but if "eject" your project or start with the React Native CLI (via react-native init) then you could use an Android specific React Native "NativeModule" to accomplish your goal. I like using the react-native-location package, which has great support on iOS for background location updates, but on Android there is a bug currently. I put together an example project which has the necessary Android specific code inside a NativeModule you could use to start from:
https://github.com/andersryanc/ReactNative-LocationSample

How to save React Native phone sensor data to an array without using state arrays (getting Maximum update depth exceeded)?

I'm having some trouble figuring out how to handle sensor data in React Native. My goal is to get phone sensor data for a limited time (let's say 10 seconds) and after completion save the list obtained to local database. However pushing new gyroscope value to state array is one option but getting maximum update depth exceeded error in there. The code is below.
import React from 'react';
import {
StyleSheet,
View,
Text,
Dimensions,
} from 'react-native';
import { Gyroscope } from 'expo-sensors';
import { Button } from "native-base";
import CountDown from 'react-native-countdown-component';
import UUID from 'pure-uuid';
import {insertGyroData} from './measureService';
const SAMPLING_RATE = 20; // defaults to 20ms
export class Measure extends React.Component {
state = {
gyroscopeData: {},
measuringStarted: false,
x_gyro: [0],
y_gyro: [0],
z_gyro: [0]
};
componentDidMount() {
this.toggleGyroscope();
}
componentWillUnmount() {
this.unsubscribeAccelerometer();
}
toggleGyroscope = () => {
if (this.gyroscopeSubscription) {
this.unsubscribeAccelerometer();
} else {
this.gyroscopeSubscribe();
}
};
gyroscopeSubscribe = () => {
Gyroscope.setUpdateInterval(20);
this.gyroscopeSubscription = Gyroscope.addListener(gyroscopeData => {
this.setState({ gyroscopeData });
});
};
unsubscribeAccelerometer = () => {
this.gyroscopeSubscription && this.gyroscopeSubscription.remove();
this.gyroscopeSubscription = null;
};
referenceMeasurementCompleted = async (x, y, z) => {
this.setState({ measuringStarted: false });
const uuid = new UUID(4).format();
await insertGyroData([uuid, 'admin', '12345', x.toString(), y.toString(), z.toString()]);
alert('Reference measurements completed');
};
render() {
let { x, y, z } = this.state.gyroscopeData;
const { x_gyro, y_gyro, z_gyro } = this.state;
if (this.state.measuringStarted) {
this.setState(({ x_gyro: [...x_gyro, x], y_gyro: [...y_gyro, y], z_gyro: [...z_gyro, z] }));
return (
<View style={styles.container}>
<Text>Gyroscope:</Text>
<Text>
x: {round(x)} y: {round(y)} z: {round(z)}
</Text>
<Text>Time spent:</Text>
<CountDown
until={5}
size={30}
onFinish={() => this.referenceMeasurementCompleted(x_gyro, y_gyro, z_gyro)}
digitStyle={{ backgroundColor: '#FFF' }}
digitTxtStyle={{ color: '#00c9ff' }}
timeToShow={['M', 'S']}
timeLabels={{ m: 'MM', s: 'SS' }}
/>
</View>
);
} else {
return (
<View style={styles.container}>
<Button
rounded
primary
onPress={() => this.setState({ measuringStarted: true })}
>
<Text style={styles.buttonText}>Start reference measurements</Text>
</Button>
</View>
);
}
}
}
function round(n) {
if (!n) {
return 0;
}
return Math.floor(n * 100) / 100;
}
const styles = StyleSheet.create({
container: {
flex: 1,
height: Dimensions.get('window').height,
backgroundColor: 'white',
alignItems: 'center',
justifyContent: 'center'
},
buttonText: {
color: 'white',
fontSize: 20,
paddingHorizontal: 10,
},
});
With the current solution I'm getting Maximum Depth Update Exceeded error
Please help!
The error is obviously coming from this block since you are changing state inside the render.
if (this.state.measuringStarted) {
this.setState(({ x_gyro: [...x_gyro, x], y_gyro: [...y_gyro, y], z_gyro: [...z_gyro, z] }));
}
One way you can try is updating the state in
componentDidUpdate(prevProps,prevState){}
But take care that it does not lead to Maximum Stack Reached error. With proper conditional statements you can set the state to new data without infinite recursion.

get active tab from Closable TabPanel in extReact sencha component

I'm making Closable TabPanel and at the moment when I click on the certain tab I will switch to the Panel of that's tab. What Panel's tab I click, that Panel opens. That's great.
but now I want to have a carousel button on the side, to list next, next, next Panel.
Which means I need to know on what panel I am at the moment (what panel is active now)? And How to tell take next panel in the row ( setPanel(activeId+1) ) ?
For now, I'm getting a list of all existing PANELS and I extract all their IDs: [45,46,47,48,49]. I'm getting it form DataBase, but I don't know how to get currently Active panel and how to say go to next!?
Any ideas?
use property: activeItem={2}
for example: <TabPanel activeItem={this.state.componentNumber} border={false} ...> </TabPanel>
App.js
import React, { Component } from 'react';
import { Button } from '#extjs/ext-react';
import ClosableComponent from './ClosableComponent';
class App extends Component {
constructor() {
super();
this.state = {
componentNumber: 2,
};
}
switchFunctionLeft = () => {
if(this.state.componentNumber === 0){
this.setState({ componentNumber: 2 })
}else{
this.setState({ componentNumber: this.state.componentNumber - 1 })
}
}
switchFunctionRight = () => {
if(this.state.componentNumber === 2){
this.setState({ componentNumber: 0 })
}else{
this.setState({ componentNumber: this.state.componentNumber + 1 })
}
}
render() {
return (
<div className="App">
<div >
<ClosableComponent componentNumber={this.state.componentNumber} />
<Button text="switch to left" handler={this.switchFunctionLeft} />
<Button text="switch to right" handler={this.switchFunctionRight}/>
</div>
</div>
);
}
}
export default App;
ClosableComponent.js
import React, {
Component
} from 'react';
import {
TabPanel,
Container,
Toolbar,
Button
} from '#extjs/ext-react';
class ClosableComponent extends Component {
nextKey = 0;
constructor() {
super();
this.state = {
type: "No",
switch: "",
tabs: [
this.nextKey++,
this.nextKey++,
this.nextKey++
]
};
}
onCloseTab = tab => {
const tabs = this.state.tabs.filter(t => t !== tab);
this.setState({
tabs
})
}
addTab = () => {
const key = this.nextKey++;
const tabs = [...this.state.tabs, key];
this.setState({tabs})
this.tabPanel.setActiveItem(tabs.indexOf(2))
return false;
}
render() {
const { tabs } = this.state;
return (
<div>
<Container layout="fit" padding = {10}>
<TabPanel
ref = {tp => this.tabPanel = tp}
_reactorIgnoreOrder
shadow
style={{ backgroundColor: 'white', "height": "200px"}}
activeItem={this.props.componentNumber}
tabBar={{
height: 48,
layout: {
pack: 'left'
},
style: {
paddingRight: '52px'
}
}}
>
{ tabs.map(key => (
<Container
title = {`Tab ${key}`}
tab = {{ flex: 1, maxWidth: 150 }}
key = {key}
layout = "center"
closable
onDestroy = {this.onCloseTab.bind(this, key)}
>
<div style = {{ whiteSpace: 'nowrap' }}>Tab {key} Content</div>
</Container>
))}
</TabPanel>
<Button
top={18}
right={20}
iconCls="x-fa fa-plus"
handler={this.addTab}
ui="alt round"
tooltip="New Tab"
/>
</Container>
</div>
);
}
}
export default ClosableComponent;