ReactGridLayout.children[0].y must be a number - react-grid-layout

I get the following error message when I'm trying to run the website in my development environment:
Uncaught Error: ReactGridLayout:
ReactGridLayout.children[0].y must be a number!
at validateLayout (app.js:6171)
at app.js:6132
at forEachSingleChild (app.js:62734)
at traverseAllChildrenImpl (app.js:62638)
at traverseAllChildrenImpl (app.js:62654)
at traverseAllChildren (app.js:62709)
at Object.forEachChildren [as forEach] (app.js:62754)
at synchronizeLayoutWithChildren (app.js:6117)
at ReactGridLayout._initialiseProps (app.js:40638)
at new ReactGridLayout (app.js:40089)
There is also an error telling me this:
app.js:77841 The above error occurred in the component:
in ReactGridLayout (created by ResponsiveReactGridLayout)
in ResponsiveReactGridLayout (created by WidthProvider)
in WidthProvider (created by Grid)
in div (created by Grid)
in Grid (created by Test)
in Test
This is my Test.js file:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import '../../../public/css/app.css';
import '../../../public/css/all.css';
import Grid from '../components/Grid';
class Test extends Component{
render() {
return (
<Grid/>
)
}
}
export default Test;
if (document.getElementById('example')) {
ReactDOM.render(<Test />, document.getElementById('example'));
}
This is my Grid.jsx file:
import '../../../public/css/all.css';
import React from 'react';
import _ from "lodash";
import {WidthProvider, Responsive} from 'react-grid-layout';
import Select from 'react-select';
import 'react-select/dist/react-select.css';
import Clock from './Clock.jsx';
import Weather from './Weather.jsx';
const ResponsiveReactGridLayout = WidthProvider(Responsive);
const originalLayouts = getFromLS("layouts") || [];
/* This class generates the layout for the web app. It renders the grid
* and it's items, but also button's and a dropdown menu, to control the grid.
*/
class Grid extends React.PureComponent {
static defaultProps = {
className: "layout",
cols: { lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
rowHeight: 100,
autoSize: true,
};
constructor(props) {
super(props);
this.state = {
items: originalLayouts.map(function(i, key, list) {
return {
i: originalLayouts[key].i,
x: originalLayouts[key].x,
y: originalLayouts[key].y,
w: originalLayouts[key].w,
h: originalLayouts[key].h,
widget: originalLayouts[key].widget,
minW: originalLayouts[key].minW,
minH: originalLayouts[key].minH,
maxH: originalLayouts[key].maxH
};
}),
selectedOption: '',
newCounter: originalLayouts.length
};
this.onAddItem = this.onAddItem.bind(this);
this.onBreakPointChange = this.onBreakPointChange.bind(this);
this.onLayoutChange = this.onLayoutChange.bind(this);
this.onLayoutReset = this.onLayoutReset.bind(this);
}
/* This function renders all grid items in the layout array. It creates a div
* with a remove button, and content. The content managed by a switch statement,
* which output is based on the widget property from the grid items.
*/
createElement(el) {
const removeStyle = {
position: 'absolute',
right: '2px',
top: 0,
cursor: 'pointer'
};
const i = el.i;
const widget = el.widget;
return (
<div key={i} data-grid={el}>
{(() => {
switch(widget) {
case 'Clock':
return <Clock/>;
case 'Photo':
return <div className='photo'></div>;
case 'Weather':
return <Weather/>;
default:
return <span>{widget}</span>;
}
})()}
<span
className='remove'
style={removeStyle}
onClick={this.onRemoveItem.bind(this, i)} >
x
</span>
</div>
);
}
/* The onAddItem() function is called when the user clicks on the 'Add Item' button.
* It adds a new grid item to the state, and takes the selected item in the dropmenu
* into account. This way the correct widget is loaded by the createElement() function.
*/
onAddItem() {
var selection = this.state.selectedOption ? this.state.selectedOption : 0;
var widgetProps = returnProps(selection.value);
if(selection) {
console.log('adding', 'n' + this.state.newCounter + '; ' + selection.value);
} else {
console.log('adding', 'n' + this.state.newCounter + '; empty');
}
this.setState({
items: this.state.items.concat({
i: 'n' + this.state.newCounter,
x: (this.state.items.length * 2) % (this.state.cols || 12),
y: Infinity,
w: widgetProps.w,
h: widgetProps.h,
widget: selection ? selection.value : '',
minW: widgetProps.minW,
minH: widgetProps.minH,
maxH: widgetProps.maxH,
}),
newCounter: this.state.newCounter + 1
});
}
/* onLayoutReset() is called when the user clicks on the 'Reset Layout' button.
* It clears the localStorage and then issues a window refresh.
*/
onLayoutReset() {
localStorage.clear();
window.location.reload();
}
/* Calls back with breakpoint and new # cols */
onBreakPointChange(breakpoint, cols) {
this.setState({
breakpoint: breakpoint,
cols: cols
});
}
/* Is called whenever the layout is changed. The for loop adds widget attribute
* from items array to objects in layout array, so that the widget props
* are also saved to localStorage. This is because objects in the layout array
* do not include a widget property by default.
*/
onLayoutChange(layout) {
this.setState({ layout: layout });
for (var i = 0; i < this.state.items.length; i++) {
layout[i].widget = this.state.items[i].widget;
}
saveToLS('layouts', layout);
}
/* When a user presses the little 'x' in the top right corner of a grid item,
* this function is called. It removes the corresponding grid item.
*/
onRemoveItem(i) {
this.setState({ items: _.reject(this.state.items, {i: i }) });
}
/* handleChange passes the selected dropdown item to the state. */
handleChange = (selectedOption) => {
this.setState({ selectedOption });
if (selectedOption) {
console.log(`Selected: ${selectedOption.label}`);
}
};
/* This render function, renders the grid, dropdown-menu, 'Add Item'-button
* and 'Reset Layout'-button. This is also where the createElement() function
* is called for each grid item.
*/
render() {
const { selectedOption } = this.state;
return (
<div>
<div className='widgetselecter'>
<Select className='dropdown'
name="form-field-name"
value={selectedOption}
onChange={this.handleChange}
options={[
{ value: 'one', label: 'One' },
{ value: 'Clock', label: 'Clock' },
{ value: 'Photo', label: 'Photo' },
{ value: 'Weather', label: 'Weather' },
]}
/>
<button className='addButton' onClick={this.onAddItem}>Add Item</button>
<button className='reset' onClick={this.onLayoutReset}>Reset Layout</button>
<span className='title'>/Dash</span>
</div>
<ResponsiveReactGridLayout
onLayoutChange={this.onLayoutChange}
onBreakPointChange={this.onBreakPointChange}
{...this.props}>
{_.map(this.state.items, el => this.createElement(el))}
</ResponsiveReactGridLayout>
</div>
);
}
}
/* Retrieve layout from local storage. */
function getFromLS(key) {
let ls = {};
if (global.localStorage) {
try {
ls = JSON.parse(global.localStorage.getItem("rgl-8")) || {};
} catch (e) {
/*Ignore*/
}
}
return ls[key];
}
/* Save layout to local storage. */
function saveToLS(key, value) {
if (global.localStorage) {
global.localStorage.setItem(
"rgl-8",
JSON.stringify({
[key]: value
})
);
}
}
/* returnProps function returns widget-specific properties like width, min width,
* heigth, etc.
*/
function returnProps(selection) {
switch(selection) {
case 'Clock':
return {
w: 1.5,
h: 1,
minW: 1.5,
minH: 1,
maxH: 1000
};
case 'Weather':
return {
w: 3,
h: 3,
minW: 3,
minH: 3,
maxH: 3
};
default:
return {
w: 2,
h: 2,
minW: 1,
minH: 1,
maxH: 1000,
};
}
}
export default Grid;
I can't remember that I changed anything in the code and I also can't find anything related to the error message on Google. Can anyone tell me more about it or explain it to me? So i can look for a solution.

Seems I had to change this bit of code:
<ResponsiveReactGridLayout
onLayoutChange={this.onLayoutChange}
onBreakPointChange={this.onBreakPointChange}
{...this.props}>
{_.map(this.state.items, el => this.createElement(el))}
>
</ResponsiveReactGridLayout>
to this:
<ResponsiveReactGridLayout
{...this.props}
onBreakpointChange={this.onBreakpointChange}
onLayoutChange={this.onLayoutChange}>
{_.map(this.state.items, el => this.createElement(el))}
</ResponsiveReactGridLayout>
I think it has something to do with the order of rules of code and then especially this part:
>
{_.map(this.state.items, el => this.createElement(el))}
because this piece is outside the <ResponsiveReactGridLayout> now. I'm not sure if this is the right solution but it works for me. So if anyone has some additional information let me know please.

Related

I tried to implement this HTML Text demo with React Konva, but in vain, because

What do I want to do:
I want to make React Konva Text editable. That is, if I double click on the Text... (I can move it to anywhere in the Konva Stage) ...I can show a text area to get the edits from the user, any changes he/she would like to do to the default text.
Conditions:
Enter key (keyboard) should produce a new line.
Double click on Text should show this text area on the same offset X and Y of this Text.
Double click on div should take back to Text. Now if there was any change done to the text, then it should get reflected on the Text component's label
I tried to implement this HTML Text demo with React Konva, but in vain, because of limitations
The html Konva demo that I want to reproduce in React Konva
Things I did:
Since HTML 5.0 is incompatible if used in conjugation with React Konva components, like Text, Image, RegularPolygon ... etc.
I used { Html } from 'react-konva-utils' so that I could move that content along with the Text component like in the demo in the link above.
Things that I observed:
The HTML can take padding and margin (we can use normal html within tag.), but not top, left.
I did try to put X property of Text in the margin top of the root div and value of Text's Y property in the margin left attribute of the same div, but had to revert as it was not close to the demo above.
Code:
import React, { useState, useRef, useEffect, Fragment } from "react";
import { Html } from 'react-konva-utils';
import { Text, Transformer } from "react-konva";
/*
Konva warning: tr.setNode(shape), tr.node(shape) and tr.attachTo(shape) methods are deprecated. Please use tr.nodes(nodesArray) instead.
*/
const KText = ({ stage, id, properties, isSelected, onSelect, onChange, setActiveText }) => {
const shapeRef = useRef();
const trRef = useRef();
const [toggleEdit, setToggleEdit] = useState(false)
useEffect(() => {
if (isSelected) {
trRef.current.nodes([shapeRef.current]);
trRef.current.getLayer().batchDraw();
}
}, [isSelected]);
// console.log("KText", `properties: ${JSON.stringify(properties)}`)
// console.log("KText", ` properties.text: ${properties.text}`)
const EditTextField = () => {
var textProps
const updateText = (data) => {
textProps = data
// console.log("EditTextField", `textProps: ${JSON.stringify(textProps)}`)
}
// var mAreaPos = areaPosition()
const areaPosition = () => {
let stage1 = stage.current.getStage()
return ({
x: stage1.container().offsetLeft + properties.x,
y: stage1.container().offsetTop + properties.y,
})
};
return (
<Html >
<div style={{
margin: "200px", padding: "20px", background: "lavender",
borderRadius: 20, borderStyle: "solid", borderColor: "green",
top: areaPosition().x, left: areaPosition().y
}}
onDoubleClick={() => setToggleEdit(!toggleEdit)}>
<label htmlFor="inputText">Please enter some text below:</label><p>
<textarea onChange={(evt) => (updateText({ text: evt.target.value, id: id }))}
id="inputText" name="inputText" rows="4" cols="50" placeholder="Please enter here" />
<br />
<button type="text" onClick={() => {
setToggleEdit(!toggleEdit)
setActiveText(textProps)
}}>Close</button>
</p>
</div>{/* */}
</Html >
)
}
const MainText = () => {
return (
<>
<Fragment>
<Text
stroke={"black"}
strokeWidth={1}
onTap={onSelect}
onClick={onSelect}
onDblClick={() => setToggleEdit(!toggleEdit)}
ref={shapeRef}
// {...shapeProps}
name="text"
x={properties.x}
y={properties.y}
text={properties.text}
fontFamily={properties.fontFamily}//"Serif"
fontSize={properties.fontSize}//50
fontWeight={properties.fontWeight} //"bold"
fillLinearGradientStartPoint={{ x: 0, y: 0 }}
fillLinearGradientEndPoint={{ x: 100, y: 100 }}
fillLinearGradientColorStops={[
0,
"rgba(0,0,0,0.7)",
1,
"rgba(255,155,255,0.5)"
]}
fillPriority={"linear-gradient"}
draggable
onDragEnd={e => {
/* onChange({
...shapeProps,
x: e.target.x(),
y: e.target.y(),
});*/
}}
onTransformEnd={e => {
// transformer is changing scale
/* const node = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
node.scaleX(1);
node.scaleY(1);
onChange({
...shapeProps,
x: node.x(),
y: node.y(),
width: node.width() * scaleX,
height: node.height() * scaleY,
}); */
}}
/>
{isSelected && <Transformer ref={trRef} />}
</Fragment>
</>
)
}
const RenderThis = () => {
let inText = "" + properties.text
if (inText.trim().length === 0 || toggleEdit) {
return (
<EditTextField />
)
} else return (
<MainText />
)
}
// rendering function
return (
<RenderThis />
);
};
export default KText;

how I can fix overlap issue in stackbarchart using vue-chartjs

I'm using latest vue-chartjs package with vue3 to create stackbarchart. I've shown the stackbarchart on my app but it's labels are overlapping. I need to know which property can add in options that can fix my issue.
<template>
<Bar
v-if="chartData != null"
:key="id"
:data="chartData"
:options="chartOptions"
/>
</template>
<script>
import { Bar, getElementAtEvent } from "vue-chartjs";
import ChartJSPluginDatalabels from "chartjs-plugin-datalabels";
import uniqueId from "lodash.uniqueid";
import { drilldown } from "#/views/Reports/js/drilldown";
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
BarElement,
CategoryScale,
LinearScale,
ArcElement
} from "chart.js";
ChartJS.register(
CategoryScale,
LinearScale,
BarElement,
Title,
Tooltip,
Legend,
ArcElement,
ChartJSPluginDatalabels
);
export default {
name: "BarChartStacked",
components: {
Bar,
},
props: ["data", "options", "reportData", "eventInfo", "item", "duringDay"],
data() {
return {
id: null,
};
},
computed:{
chartData() { return this.data; /* mutable chart data */ },
chartOptions() { return this.options; /* mutable chart options */ }
},
mounted() {
this.id = uniqueId();
this.chartOptions.plugins.responsive = true;
if (this.reportData && this.reportData.dataFilter) {
if (this.item.conditions) {
// change cursor to pointer if element is clickable
this.chartOptions.hover = {
onHover: function(e) {
var point =getElementAtEvent(e);
if (point.length) e.target.style.cursor = 'pointer';
else e.target.style.cursor = 'default';
}
}
this.chartOptions.onClick = this.handle;
}
} else {
this.chartOptions.hover = {}
}
},
The stackbarchart should display value for the top most graph only like mention in the picture.

Mouse Out of Element and Mouse Into Another Element Does Not Reset State

Code: https://codesandbox.io/s/objective-darwin-w0i5pk?file=/src/App.js
Description:
This is just 4 gray squares that each get their own shade of gray. I want to change the background color of each square when the user hovers over each, but I want the hover color to be +10 in RGB of what it was originally.
Issue:
When I mouse/hover out of one of the gray squares and mouse/hover into another gray square, the first square does not switch back to its initial color state.
Help:
Can someone explain why it is doing this and how to fix it because I have no idea?
Note:
I am trying not to use CSS for the hover because I am specifying the backgroundColor with JS.
import React, { useState } from "react";
import "./styles.css";
const tabs = [
{ name: "1", img: [] },
{ name: "2", img: [] },
{ name: "3", img: [] },
{ name: "4", img: [] }
];
const initialState = {};
tabs.forEach((t, i) => {
initialState[i] = false;
});
export default function App() {
const [hover, setHover] = useState(initialState);
return (
<div className="App">
{tabs.map((t, i) => {
const v = 50 - (i + 1) * 10;
const val = hover[i] ? v + 10 : v;
return (
<div
key={t.name}
className="tab"
onMouseOver={() => {
setHover({
...hover,
[i]: true
});
}}
onMouseLeave={() => {
setHover({
...hover,
[i]: false
});
}}
onMouseOut={() => {
setHover({
...hover,
[i]: false
});
}}
style={{
backgroundColor: `rgb(${val}, ${val}, ${val})`,
height: "100px",
width: "100px"
}}
>
<p>{t.name}</p>
</div>
);
})}
</div>
);
}
.App {
font-family: sans-serif;
text-align: center;
margin: 0;
padding: 0;
}
* {
margin: 0;
padding: 0;
}
This picture only shows the initial state:
setState calls are not what a human would consider "immediate". Instead, the calls to the state setter as queued inside React internal mechanisms. Consider this:
const [state, setState] = useState(0)
// somewhere
setState(state + 1)
setState(state + 1)
In this case, you do not end up with 2 but 1, because while you call setState twice to increment by one, you really are calling it as:
setState(1)
setState(1)
This is the exact issue in your code with the callbacks, you have
// enter
setState({ ...state, [i]: true })
// leave
setState({ ...state, [i]: false })
so when both get called, you apply the "leave" with the wrong previous state.
This is why setState has another pattern, setState(prevState => nextState)
setState(prevState => prevState + 1)
setState(prevState => prevState + 1)
Like this, you do end up with the value 2 because the second call is then using the "correct" previous state.
In your case, you need:
// enter
setState(prevState => ({ ...prevState, [i]: true }))
// leave
setState(prevState => ({ ...prevState, [i]: false }))
This is happening because you also keep the previous values in your state. You should update in this way
onMouseOver={() => {
setHover({
[i]: true
});
}}
onMouseLeave={() => {
setHover({
[i]: false
});
}}
onMouseOut={() => {
setHover({
[i]: false
});
}}

ReactJS - Rerender component

I'm trying to create a menu in ReactJS that has a SideBar with icons and also a BurgerMenu in which I display titles that match the icons. When selecting a menu item, either from the SideBar or the BurgerMenu, it changes its color. If I select an item from the burger menu, everything works fine, but if I select it from the Sidebar, then in the burger menu, the previous item remains colored. It seems that the items in the burger menu are not rerendered and I can't find a solution to this.
Here is the code:
import React from 'react';
import styled from "styled-components";
import NavItem from "./NavItem";
import BurgerSideNav from "./burger-nav/BurgerSideNav";
/* This defines the actual bar going down the screen */
const StyledSideNav = styled.div`
position: fixed; /* Fixed Sidebar (stay in place on scroll and position relative to viewport) */
height: 100%;
width: 75px; /* Set the width of the sidebar */
z-index: 1; /* Stay on top of everything */
top: 3.4em; /* Stay at the top */
background-color: #222; /* Black */
overflow-x: hidden; /* Disable horizontal scroll */
padding-top: 10px;
`;
class SideNav extends React.Component {
constructor(props) {
super(props);
this.state = {
activePath: this.props.activePath,
items: [
{
path: '/', /* path is used as id to check which NavItem is active basically */
name: 'Home',
css: 'fa fa-fw fa-home',
key: 1 /* Key is required, else console throws error. Does this please you Mr. Browser?! */
},
{
path: '/news',
name: 'News',
css: 'fas fa-newspaper',
key: 2
},
{
path: '/info',
name: 'Info',
css: 'fas fa-info',
key: 3
},
{
path: '/profile',
name: 'Profile',
css: 'fas fa-id-card',
key: 4
},
{
path: '/coordinator',
name: 'Coordinator',
css: 'fas fa-user-tie',
key: 5
},
{
path: '/contact',
name: 'Contact',
css: 'fas fa-address-book',
key: 6
},
]
}
}
onItemClick = (path) => {
this.setState({activePath: path}); /* Sets activePath which causes rerender which causes CSS to change */
};
render() {
const { items, activePath } = this.state;
return (
<div>
<StyledSideNav>
<BurgerSideNav
activePath = {activePath}
onItemClick={this.onItemClick}
/>
{
/* items = just array AND map() loops thru that array AND item is param of that loop */
items.map((item) => {
/* Return however many NavItems in array to be rendered */
return (
<NavItem
path={item.path}
name={item.name}
css={item.css}
onItemClick={this.onItemClick} /* Simply passed an entire function to onClick prop */
active={item.path === activePath}
key={item.key}
/>
)
})
}
</StyledSideNav>
</div>
);
}
}
export default SideNav
import React from "react"
import "../sideNav.css"
import BurgerNavItem from "./BurgerNavItem";
class BurgerSideNav extends React.Component {
constructor(props) {
super(props);
this.state = {
showNav: false,
activePath: this.props.activePath,
items: [
{
path: '/',
name: 'Acasa',
css: 'fa fa-fw fa-home',
key: 1
},
{
path: '/news',
name: 'Noutati',
css: 'fas fa-newspaper',
key: 2
},
{
path: '/info',
name: 'Despre lucrare',
css: 'fas fa-info',
key: 3
},
{
path: '/profile',
name: 'Profil student',
css: 'fas fa-id-card',
key: 4
},
{
path: '/coordinator',
name: 'Coordonator',
css: 'fas fa-user-tie',
key: 5
},
{
path: '/contact',
name: 'Contact',
css: 'fas fa-address-book',
key: 6
},
]
};
}
openNavClick = e => {
e.preventDefault();
this.openNav()
};
closeNavClick = e => {
e.preventDefault();
this.closeNav()
};
openNav = () => {
this.setState({
showNav: true
});
document.addEventListener("keydown", this.handleEscKey)
};
closeNav = () => {
this.setState({
showNav: false
});
document.removeEventListener("keydown", this.handleEscKey)
};
handleEscKey = e => {
if (e.key === "Escape") {
this.closeNav()
}
};
onItemClick = (path) => {
const {onItemClick} = this.props;
this.setState({ activePath: path });
onItemClick(path);
};
render() {
const { items, activePath, showNav } = this.state;
let navCoverStyle = { width: showNav ? "100%" : "0" }
let sideNavStyle = { width: showNav ? "250px" : "0" }
return (
<React.Fragment>
<span onClick={this.openNavClick}>
<i className="fas fa-bars open-nav"/>
</span>
<div
onClick={this.navCoverClick}
class="nav-cover"
style={navCoverStyle}
/>
<div name="side-nav" class="side-nav" style={sideNavStyle}>
<a href="#" onClick={this.closeNavClick} class="close-nav">
×
</a>
{
items.map((item) => {
return (
<BurgerNavItem
path={item.path}
name={item.name}
css={item.css}
onItemClick={this.onItemClick}
active={item.path === activePath}
key={item.key}/>
)
})
}
</div>
})
</React.Fragment>
)
}
}
export default BurgerSideNav
If I understand the question correctly, if you want to re-render, you can pass the activePath as part of the key to easily force a re-render:
return (
<NavItem
path={item.path}
name={item.name}
css={item.css}
onItemClick={this.onItemClick}
active={item.path === activePath}
key={`${item.key}-${activePath}`}
/>
);
One thing to note is that you probably only need activePath defined in the Parent Component (SideNav), not in both if you don't want both to be styled, hence why both stay 'enabled'.
Also, your items won't change, so they should be constant somewhere else, instead of being part of your state. Maybe in a constants file:
// PathContants.js
export default PathConstants = [
{
id: 1,
path: '/home',
},
..
];
If the paths are always unique, you can just use the path itself, as a key key={item.path}, since the key is actually a string. No need to store the key itself.
in BurgerSideNav component you have set the state in the constructor function saying
activePath: this.props.activePath
but when the state changes in the StyledSideNav component you are not setting the state again, to set the state again, implement componentWillReceiveProps
eg:
componentWillReceiveProps(nextProps) {
if (this.props.activePath !== nextProps.activePath) {
this.setState({
activePath: nextProps.activePath
})
}
}

calling function from PhotoGrid render function Library

i am using a PhotoGrid Library in react native to populate the list of photo on my apps. how to call a function from the render function ? it show this error when i call a function called "deva" on my OnPress method in <Button onPress={()=>{this.deva()}}><Text>Bondan</Text></Button> . here is my code...
import React from 'react';
import { StyleSheet, Text, View, WebView, TouchableOpacity, Image, Alert, Dimensions} from 'react-native';
import {DrawerNavigator} from 'react-navigation'
import {Container, Header, Button, Icon, Title, Left, Body, Right, Content} from 'native-base'
import PhotoGrid from 'react-native-photo-grid'
import HomeScreen from './HomeScreen'
export default class Recomended extends React.Component {
constructor() {
super();
this.state = { items: [],
nama : ""
}
}
goToBufetMenu(){
this.props.navigation.navigate("BufetMenu");
}
componentDidMount() {
// Build an array of 60 photos
let items = Array.apply(null, Array(60)).map((v, i) => {
return { id: i, src: 'http://placehold.it/200x200?text='+(i+1) }
});
this.setState({ items });
//this.setState({ nama: "Bondan"});
//this.props.navigation.navigate("BufetMenu");
}
deva() {
Alert.alert('deva');
}
render() {
return (
<Container style={styles.listContainer}>
<PhotoGrid
data = { this.state.items }
itemsPerRow = { 3 }
itemMargin = { 3 }
renderHeader = { this.renderHeader }
renderItem = { this.renderItem }
style={{flex:2}}
/>
</Container>
);
}
renderHeader() {
return(
<Button onPress={()=>{this.deva()}}><Text>Bondan</Text></Button>
);
}
renderItem(item, itemSize) {
return(
<TouchableOpacity
key = { item.id }
style = {{ width: itemSize, height: itemSize }}
onPress = { () => {
this.deva();
}}>
<Image
resizeMode = "cover"
style = {{ flex: 1 }}
source = {{ uri: item.src }}
/>
<Text>{item.src}</Text>
</TouchableOpacity>
)
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
height: 587,
},
gridText: {
color: '#fff',
textAlign: 'center',
fontStyle: 'normal',
fontSize : 12
},
listContainer: {
height: Dimensions.get('window').height - (Dimensions.get('window').height*53/100),
}
});
You are loosing context of this. You need to either use arrow functions or bind the functions.
Example
constructor() {
super();
this.state = { items: [],
nama : ""
};
this.renderHeader = this.renderHeader.bind(this);
this.renderItem = this.renderItem.bind(this);
}
OR
renderHeader = () => {
// rest of your code
}
renderItem = (item, itemSize) => {
// rest of your code
}
Either change your deva method definition to an arrow function -
deva= () => {
Alert.alert('deva');
}
Or bind the deva method to this inside your constructor
constructor() {
super();
this.state = { items: [],
nama : ""
}
this.deva = this.deva.bind(this)
}
You get the error because when the deva method is invoked using this.deva(), the javascript runtime cannot find the property/function deva on the this it's called with (which is the anonymous callback passed to onPress in this case). But if you bind this to deva beforehand, the correct this is being searched by the javascript runtime.