So my understanding according to this post:
React Redux: Uncaught Invariant Violation (Objects are not valid as a React child)
is that my {job.title} and so on are objects and React won't let me just plop an object into the render output, but I guess I don't have enough experience to understand fully as to why I am getting this error in the first place nor how to fix it.
So in this screen:
import React, { Component } from "react";
import { View, Text, Platform } from "react-native";
import { MapView } from "expo";
import { connect } from "react-redux";
import { Card, Button } from "react-native-elements";
import Swipe from "../components/Swipe";
import * as actions from "../actions";
class DeckScreen extends Component {
renderCard(job) {
return (
<Card title={job.title}>
<View style={{ height: 300 }}>
<MapView
scrollEnabled={false}
style={{ flex: 1 }}
cacheEnabled={Platform.OS === "android" ? true : false}
/>
</View>
<View style={styles.detailWrapper}>
<Text>{job.company}</Text>
<Text>{job.post_date.toString()}</Text>
</View>
<Text>
{job.description.replace(/<span>/g, "").replace(/<\/span>/g, "")}
</Text>
</Card>
);
}
renderNoMoreCards() {
return <Card title="No more jobs" />;
}
render() {
return (
<View>
<Swipe
data={this.props.jobs}
renderCard={this.renderCard}
renderNoMoreCards={this.renderNoMoreCards}
keyProp="id"
/>
</View>
);
}
}
const styles = {
detailWrapper: {
flexDirection: "row",
justifyContent: "space-around",
marginBottom: 10
}
};
function mapStateToProps({ jobs }) {
return { jobs: jobs.listing };
}
export default connect(
mapStateToProps,
actions
)(DeckScreen);
and in my component:
import React, { Component } from "react";
import {
View,
Animated,
PanResponder,
Dimensions,
LayoutAnimation,
UIManager
} from "react-native";
const SCREEN_WIDTH = Dimensions.get("window").width;
const SWIPE_THRESHOLD = 0.25 * SCREEN_WIDTH;
const SWIPE_OUT_DURATION = 250;
class Swipe extends Component {
static defaultProps = {
onSwipeRight: () => {},
onSwipeLeft: () => {},
keyProp: "id"
};
constructor(props) {
super(props);
const position = new Animated.ValueXY();
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
onPanResponderMove: (event, gestureState) => {
position.setValue({ x: gestureState.dx, y: gestureState.dy });
},
onPanResponderRelease: (event, gestureState) => {
if (gestureState.dx > SWIPE_THRESHOLD) {
this.forceSwipe("right");
} else if (gestureState.dx < -SWIPE_THRESHOLD) {
this.forceSwipe("left");
} else {
this.resetPosition();
}
}
});
this.state = { panResponder, position, index: 0 };
}
componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.props.data) {
this.setState({ index: 0 });
}
}
componentWillUpdate() {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
LayoutAnimation.spring();
}
forceSwipe(direction) {
const x = direction === "right" ? SCREEN_WIDTH : -SCREEN_WIDTH;
Animated.timing(this.state.position, {
toValue: { x, y: 0 },
duration: SWIPE_OUT_DURATION
}).start(() => this.onSwipeComplete(direction));
}
onSwipeComplete(direction) {
const { onSwipeLeft, onSwipeRight, data } = this.props;
const item = data[this.state.index];
direction === "right" ? onSwipeRight(item) : onSwipeLeft(item);
this.state.position.setValue({ x: 0, y: 0 });
this.setState({ index: this.state.index + 1 });
}
resetPosition() {
Animated.spring(this.state.position, {
toValue: { x: 0, y: 0 }
}).start();
}
getCardStyle() {
const { position } = this.state;
const rotate = position.x.interpolate({
inputRange: [-SCREEN_WIDTH * 1.5, 0, SCREEN_WIDTH * 1.5],
outputRange: ["-120deg", "0deg", "120deg"]
});
return {
...position.getLayout(),
transform: [{ rotate }]
};
}
renderCards() {
if (this.state.index >= this.props.data.length) {
return this.props.renderNoMoreCards();
}
return this.props.data
.map((item, i) => {
if (i < this.state.index) {
return null;
}
if (i === this.state.index) {
return (
<Animated.View
key={item[this.props.keyProp]}
style={[this.getCardStyle(), styles.cardStyle]}
{...this.state.panResponder.panHandlers}
>
{this.props.renderCard(item)}
</Animated.View>
);
}
return (
<Animated.View
key={item[this.props.keyProp]}
style={[styles.cardStyle, { top: 10 * (i - this.state.index) }]}
>
{this.props.renderCard(item)}
</Animated.View>
);
})
.reverse();
}
render() {
return <View>{this.renderCards()}</View>;
}
}
const styles = {
cardStyle: {
position: "absolute",
width: SCREEN_WIDTH
}
};
export default Swipe;
when I do a console.log(job); it renders everything you would see in the API endpoint:
And when I do a console.log(job.title); I get this:
[03:46:03] Front End Designer Summer Internship
[03:46:03] Senior UX / Product Designer - Investments AI team
[03:46:03] Technical AV Analyst
[03:46:03] Senior Designer
[03:46:03] Junior Developer
[03:46:03] Webinar Producer + Host
[03:46:03] Front End Web Developer
[03:46:03] Android Developer needed for React Native project
[03:46:03] UX Designer
[03:46:03] Software Product Engineer - Reinventing VC
Along the lines of doing a console.log(job);, I decided to then do a console.log(job.title); and I got back:
[03:46:03] Front End Designer Summer Internship
[03:46:03] Senior UX / Product Designer - Investments AI team
[03:46:03] Technical AV Analyst
[03:46:03] Senior Designer
[03:46:03] Junior Developer
[03:46:03] Webinar Producer + Host
[03:46:03] Front End Web Developer
[03:46:03] Android Developer needed for React Native project
[03:46:03] UX Designer
[03:46:03] Software Product Engineer - Reinventing VC
so that was not the problem, then I decided to move further and do a console.log(job.company); and I got back:
[03:48:25] Object {
[03:48:25] "id": "planningcenter",
[03:48:25] "location": Object {
[03:48:25] "id": "carlsbadca",
[03:48:25] "name": "Carlsbad, CA",
[03:48:25] },
[03:48:25] "logo": "https://d2fcz5no062gar.cloudfront.nethttps://authenticjobs.s3.amazonaws.com/uploads/logos/edc867ddb3b30a5c8f8819d1a1347346/thumb/PC -Icon -- color .png",
[03:48:25] "name": "Planning Center",
[03:48:25] "tagline": "Software designed to help church volunteers",
[03:48:25] "type": null,
[03:48:25] "url": "http://planning.center",
[03:48:25] }
[03:48:26] Object {
[03:48:26] "id": "americaninternationalgroupaig",
[03:48:26] "location": Object {
[03:48:26] "id": "175waterstreetnewyorkny10038",
[03:48:26] "name": "175 Water Street New York, NY 10038",
[03:48:26] },
[03:48:26] "logo": "https://d2fcz5no062gar.cloudfront.net/assets/images/defaults/company-blank.png",
[03:48:26] "name": "American International Group (AIG)",
[03:48:26] "tagline": "We're a 100 year old insurance company with operations globally!",
[03:48:26] "type": null,
[03:48:26] "url": "https://www.aig.com/about-us",
[03:48:26] }
[03:48:26] Object {
[03:48:26] "id": "universityofcalifornialosangeles",
[03:48:26] "location": Object {
[03:48:26] "id": "losangelesca",
[03:48:26] "name": "Los Angeles, CA",
bingo! there is the problem, returning objects on {job.company}, so then I decided to try console.log(job.company.id); and got:
[03:49:14] planningcenter
[03:49:15] americaninternationalgroupaig
[03:49:15] universityofcalifornialosangeles
[03:49:15] whitneymuseumofamericanart
[03:49:15] studioality
[03:49:15] convertkit
[03:49:15] metropolitanareaplanningcouncil
[03:49:15] jenzy
[03:49:15] cfainstitute
[03:49:15] nfx
boom! problem solved and my UI is rendering now.
Related
How to modify series and options, i want to make chart type=treemap on react hooks like this
i have name, user and percent on api.
{
"data": [
{
"id": "1",
"name": "Pisces",
"user": "95",
"percent": "3.15%",
},
{
"id": "2",
"name": "Leo",
"user": "50",
"percent": "2.35%",
},
{
"id": "3",
"name": "Capricorn",
"user": "91",
"percent": "3.12%",
}
]
}
and source for apex https://apexcharts.com/docs/chart-types/treemap-chart/
import React, { useState,useEffect } from 'react';
import axios from 'axios';
import './App.css';
import Chart from 'react-apexcharts'
import icUser from './image/profile-user.png'
import icChart from './image/pie-chart.png'
const App =()=> {
const [dataUser,setDataUser]=useState([])
useEffect(() => {
axios.get("http://localhost:4000/data")
.then(response =>{
setDataUser(response.data)
}).catch(e => {
alert(e);
})
}, [])
const series = {.....}
const options = {.....}
return (
<div>
<Chart options={options} series={series} height={350} type="treemap"/>
</div>
)
}
export default App
In series you need to pass an array like this, Where x is the name, and y percentage. and In option you can modify the treemap chart like change height, type, plotOptions and more...
const App = () => {
const [dataUser, setDataUser] = useState([])
useEffect(() => {
axios.get("http://localhost:4000/data")
.then(response => {
setDataUser(response.data)
}).catch(e => {
alert(e);
})
}, [])
const seriesData = [];
const options = {}
dataUser.map((val) => {
seriesData.push(
{
x: val.name, //
y: val.percent //
}
);
});
const series = [{ data: seriesData }];
return (
<div>
<Chart options={options} series={series} height={350} type="treemap" />
</div>
)
}
export default App
In my use case I need to create interactive farm maps where I can select fields.
I'm trying to do this using the react-simple-maps component.
what was done
I created the farm Shapfile map in QGIS.
Converted to TopoJSON using Mapshaper as per this tutorial.
However the map does not render correctly, see CodeSandbox.
What can I be missing?
Is this the best component to use in this case?
Map error
TopoJSON Map
{
"type": "Topology",
"arcs": [
[
[0, 62],
[51, 60],
[60, -46],
[-85, -76],
[-26, 62]
],
[
[112, 77],
[-60, 44],
[92, 110],
[57, -40],
[0, -2],
[-66, -60],
[14, -19],
[-37, -33]
]
],
"transform": {
"scale": [15.721852200470671, 19.17233904106825],
"translate": [-65942.30731638917, 8482615.288037943]
},
"objects": {
"Contorno_UTM": {
"type": "GeometryCollection",
"geometries": [
{
"arcs": [[0]],
"type": "Polygon",
"properties": { "id": 1, "area_ha": 197.4585 }
},
{
"arcs": [[1]],
"type": "Polygon",
"properties": { "id": 2, "area_ha": 299.0857 }
}
]
}
}
}
React Simple Map component
import React, { memo } from "react";
import {
ZoomableGroup,
ComposableMap,
Geographies,
Geography
} from "react-simple-maps";
import map from "./map.json";
const geoUrl = map;
const rounded = (num) => {
if (num > 1000000000) {
return Math.round(num / 100000000) / 10 + "Bn";
} else if (num > 1000000) {
return Math.round(num / 100000) / 10 + "M";
} else {
return Math.round(num / 100) / 10 + "K";
}
};
const MapChart = ({ setTooltipContent }) => {
return (
<>
<ComposableMap data-tip="" projectionConfig={{ scale: 200 }}>
<ZoomableGroup>
<Geographies geography={geoUrl}>
{({ geographies }) =>
geographies.map((geo) => (
<Geography
key={geo.rsmKey}
geography={geo}
onMouseEnter={() => {
const { id, area_ha } = geo.properties;
setTooltipContent(`${id} — ${rounded(area_ha)}`);
}}
onMouseLeave={() => {
setTooltipContent("");
}}
style={{
default: {
fill: "#D6D6DA",
outline: "none"
},
hover: {
fill: "#F53",
outline: "none"
},
pressed: {
fill: "#E42",
outline: "none"
}
}}
/>
))
}
</Geographies>
</ZoomableGroup>
</ComposableMap>
</>
);
};
export default memo(MapChart);
I managed to solve it, the problem was in the project's SRC configuration, it was UTM and the correct is WGS 84, I changed, exported in GeoJSON format and converted to TopoJSON using Mapshaper, changed the projections and rotation and now everything is ok as you can see in CodeSandbox.
projection="geoAzimuthalEqualArea"
projectionConfig={{
rotate: [56.22, 13.66, 0],
scale: 360000
}}
I want to call the function using the key name from the array on the tab.screen component.
my code is like this:
import React from 'react';
import { Text, View, TouchableOpacity, Modal } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
import Icon from 'react-native-vector-icons/FontAwesome5';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Home from '../mainscreen/GeneralScreen';
import Core from '../mainscreen/CoreScreen';
import Docs from '../mainscreen/GeneralScreen';
import ESS from '../mainscreen/GeneralScreen';
import General from '../mainscreen/GeneralScreen';
import HR from '../mainscreen/GeneralScreen';
import Payroll from '../mainscreen/GeneralScreen';
import Server from '../mainscreen/CoreScreen';
const Tab = createBottomTabNavigator();
const tabcomponents = {
"Home" : Home,
"Core" : Core,
"Docs" : Docs,
"ESS" : ESS,
"General" : General,
"HR" : HR,
"Payroll" : Payroll,
"Server" : Server
};
class TabNavigator extends React.Component {
constructor() {
super();
this.state = {
dashboardtab:[],
}
this.tabnavigatorasync();
}
tabnavigatorasync = async () => {
try {
const dashboardtab = await AsyncStorage.getItem('dashboardtab');
const dashboardtabParse = JSON.parse(dashboardtab);
this.setState({dashboardtab: dashboardtabParse});
//console.log('isi dari tab navigator: ', this.state.dashboardtab);
//console.log('------------------------------------------------');
//console.log('isi dari tab navigator 2: ', this.state.dashboardtab2[0].label);
} catch (error) {
}
}
render(){
//console.log('dashboardtab', this.state.dashboardtab);
const tabnavigatorRender = this.state.dashboardtab.map((item, index) =>
const tabcomponentsrender = tabcomponents[item.admintab.label];
return <Tab.Screen name={item.admintab.label} component={tabcomponentsrender} key={index}/>
);
return(
<Tab.Navigator>
{tabnavigatorRender}
</Tab.Navigator>
)
}
}
export default TabNavigator;
the result appears an error like this:
Invariant Violation: View config getter callback for component Home must be a function (received undefined)
This is the contents of the array:
[
{
"tab_id": "home",
"order": 10,
"admintab": {
"label": "Home",
}
},
{
"tab_id": "core",
"order": 1,
"admintab": {
"label": "Core",
}
},
{
"tab_id": "docs",
"order": 2,
"admintab": {
"label": "Docs",
}
},
{
"tab_id": "ess",
"order": 50,
"admintab": {
"label": "ESS",
}
},
{
"tab_id": "general",
"order": 45,
"admintab": {
"label": "General",
}
},
{
"tab_id": "hr",
"order": 40,
"admintab": {
"label": "HR",
}
},
{
"tab_id": "payroll",
"order": 42,
"admintab": {
"label": "Payroll",
}
},
{
"tab_id": "server",
"order": 70,
"admintab": {
"label": "Server",
}
}
]
is there something wrong with the code i made?
In your case, you want to use Dynamic Component Names.
First of all import your tabs.
import Home from 'path_to_your_tab_component/Home';
import Core from 'path_to_your_tab_component/Core'; // and so on
Then pass them in the object and give them a key based on their names.
const components = {
"Home": Home
"Core": Core // and so on
}
Inside the map, create a component from this object.
const tabnavigator = this.state.dashboardtab.map((item, index) => {
const TabComponent = components[item.admintab.label];
return <Tab.Screen name={item.admintab.label} component={TabComponent} key={index}/>
});
I'm pretty new to D3.js library. I've been trying to implement it in React JS project.
I'm just using d3 functions for calculations and rendering part will be done by React JS.
I successfully created a tree but I'm getting links with weird style (Not as expected)
This is what I expected (As per D3 Documentation)
But I got the below tree :(
Also I can't able to collapse or expand...
You can find the code below.
componentDidMount() {
const width = 800, height = 800;
const tree = d3.tree().size([height, width - 160]);
const stratify = d3.stratify().id((d) => {
return d.name;
}).parentId((d) => {
return d.parent;
});
const root = stratify(this.state.data)
.sort((a, b) => {
return (a.height - b.height) || a.id.localeCompare(b.id);
});
this.setState({ paths: tree(root).links() });
this.setState({ nodes: root.descendants() })
}
render() {
let paths = this.state.paths && this.state.paths.map(item => {
let d = d3
.linkHorizontal()
.x((d) => {
return d.y;
})
.y((d) => {
return d.x;
});
return <path className='link' d={d(item)} />
})
let nodes = this.state.nodes && this.state.nodes.map((node, i) => {
return <g key={node.id} className={"node" + node.children ? " node--internal" : " node--leaf"}
transform={`translate(${node.y}, ${node.x})`}>
<circle r="10" style={{ 'fill': node.children ? 'lightsteelblue' : 'black' }} />
<text y="0" dy="0" textAnchor="middle"
style={{ 'fillOpacity': 1 }}>{node.name}</text>
</g>
})
return (
<svg className="tree-chart-basic" ref={(r) => this.chartRf = r} style={{ width: '800px', height: '800px' }}>
<g transform='translate(20,20)'>
{nodes}
{paths}
</g>
</svg>
);
}
this.state.data will be having the array as follows
[
{ "name": "ProjectA", "parent": "" },
{ "name": "ApplicationA", "parent": "ProjectA" },
{ "name": "EnvironmentB", "parent": "ProjectA" },
{ "name": "TierC", "parent": "ApplicationA" },
{ "name": "TierD", "parent": "ApplicationA" },
{ "name": "TierE", "parent": "ApplicationA" },
{ "name": "ServiceF", "parent": "EnvironmentB" },
{ "name": "ContainerG", "parent": "EnvironmentB" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" },
{ "name": "ContainerH", "parent": "TierE" }
]
How can I get the expected Tree in my code?
How can I get collapse/expand functionality?
Please tell me what I'm doing wrong. Thanks for the time. (:
I finally got the solution to my answer.
I need to specify the style to <path> tag as follows.
<path
fill="none"
stroke="#97a6ff"
strokeWidth="2px"
/>
I had the same issue when using react-tree-graph which uses d3 under the hood. Luckily you can add custom classes to customize everything. I use css-modules so I had to escape the global classnames.
import { Tree } from 'react-tree-graph';
import style from './tree.module.scss';
// ...
<Tree svgProps={{ className: style.acmeProductTree }} />
// tree.module.scss
svg.acmeProductTree path:global(.link) {
fill: none;
stroke: #2593b8;
stroke-width: 1.5px;
}
As I found out just now you can alternatively apply a custom class directly to the path element:
<Tree pathProps={{ className: style.acmeProductTreeLink }} />
.acmeProductTreeLink {
fill: none;
stroke: #2593b8;
stroke-width: 1.5px;
}
I am using react-mapbox-gl#3.4.1 and supercluster#3.0.2 to get the list of features in a clicked cluster.
I saw that in V3.0.0 of supercluster the zoom factor is encoded into the cluster_id.
I am using a GeoJSONLayer to do the clustering (which may not be the right way to do it for supercluster) and the correct features are not returned OnClick.
Clicking some clusters throws a run-time error:
'No cluster with the specified id'.
Any idea why no features or the wrong features are being returned?
import React, { Component } from 'react';
import ReactMap, { Layer, GeoJSONLayer } from 'react-mapbox-gl';
import SuperCluster from 'supercluster';
const accessToken = "pk.eyJ1IjoiYWxleDMxNjUiLCJhIjoiY2o0MHp2cGtiMGFrajMycG5nbzBuY2pjaiJ9.QDApU0XH2v35viSwQuln5w";
const style = "mapbox://styles/mapbox/streets-v9";
const Map = ReactMap({
accessToken
});
const featureCollection = {
type: 'FeatureCollection',
features: [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-104.9847, 39.73915] },
"properties": { "id": 1 }
},
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-104.967478, 39.732269] },
"properties": { "id": 2 }
}
]
}
const mapStyle = {
height: '100vh',
width: '100vw'
};
const cluster = SuperCluster({
maxZoom: 14,
radius: 50,
});
class App extends Component {
componentWillMount () {
cluster.load(featureCollection.features);
}
onClick (evt) {
const map = evt.target;
const features = map.queryRenderedFeatures(evt.point, { layers: ['clustered_layer'] });
if(!features.length) {
return;
}
const feature = features[0];
const cluster_id = feature.properties.cluster_id;
const all_features = cluster.getLeaves(cluster_id, 500);
}
onStyleLoad (map) {
map.on('click', 'clustered_layer', this.onClick.bind(this));
}
render() {
return (
<Map
center={ [-104.9847, 39.73915] }
containerStyle={mapStyle}
onStyleLoad={ this.onStyleLoad.bind(this) }
style={style}
zoom={[5]}
>
<GeoJSONLayer
id="source_id"
data={ featureCollection }
sourceOptions={{
cluster: true,
clusterMaxZoom: 14,
clusterRadius: 50
}}
/>
<Layer
id="clustered_layer"
type="circle"
sourceId="source_id"
filter={ ['has', 'point_count']}
paint={{
'circle-color': '#0991dd',
'circle-radius': {
property: 'point_count',
type: 'interval',
stops: [
[0, 20],
[100, 30],
[750, 40]
]
}
}}
/>
<Layer
id="unclustered_layer"
type="circle"
sourceId="source_id"
paint={{
'circle-color': '#3fff80',
'circle-radius': 18
}}
filter={ ['!has', 'point_count'] }
/>
<Layer
id="text_layer"
type="symbol"
sourceId="source_id"
layout={{
'text-field': '{point_count}',
'text-size': 12
}}
filter={ ['has', 'point_count'] }
/>
</Map>
);
}
}
export default App;
WebpackBin showing this issue
Any help would be appreciated.
Update
I was able to encode the cluster_id the way supercluster does it and get the clustered items.
const cluster_id = (id << 5) + (zoom + 1);
Thanks