Related
If I click on a certain product I get the following 404 error:
If I click on one of the first 6 products which guide me to the link listed within the screenshot above, i get an error regarding my qty being undefined as follows:
and when i close out of the error I am left with this:
AGAIN - I have included my gatsby-node.js as well as my QtyButton.js code below... I also have linked to my github repo here with the most updated code...
gatsby-node.js:
/**
* Implement Gatsby's Node APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/node-apis/
*/
// You can delete this file if you're not using it
exports.createPages = async({
graphql,
actions
}) => {
const {
createPage
} = actions
const result = await graphql(
`
{
products: allStrapiProduct {
edges {
node {
name
strapiId
description
category {
name
}
variants {
id
color
size
style
price
images {
localFile {
childImageSharp {
gatsbyImageData
}
}
}
}
}
}
}
categories: allStrapiCategory {
edges {
node {
strapiId
name
description
filterOptions {
Size {
checked
label
}
Style {
checked
label
}
Color {
checked
label
}
}
}
}
}
}
`
)
if (result.errors) {
throw result.errors
}
const products = result.data.products.edges
const categories = result.data.categories.edges
products.forEach(product => {
createPage({
path: `/${product.node.category.name.toLowerCase()}/${
product.node.name.split(' ')[0]
}`,
component: require.resolve('./src/templates/ProductDetail.js'),
context: {
name: product.node.name,
id: product.node.strapiId,
category: product.node.category.name,
description: product.node.description,
variants: product.node.variants,
product,
},
})
})
categories.forEach(category => {
createPage({
path: `/${category.node.name.toLowerCase()}`,
component: require.resolve('./src/templates/ProductList.js'),
context: {
name: category.node.name,
description: category.node.description,
id: category.node.strapiId,
filterOptions: category.node.filterOptions,
},
})
})
}
exports.onCreateWebpackConfig = ({
stage,
loaders,
actions
}) => {
if (stage === 'build-html') {
actions.setWebpackConfig({
module: {
rules: [{
test: /react-spring-3d-carousel/,
use: loaders.null()
}],
},
})
}
}
QtyButton.js
import React, {
useState,
useEffect,
useContext
} from 'react'
import clsx from 'clsx'
import Grid from '#material-ui/core/Grid'
import Typography from '#material-ui/core/Typography'
import Button from '#material-ui/core/Button'
import ButtonGroup from '#material-ui/core/ButtonGroup'
import Badge from '#material-ui/core/Badge'
import {
makeStyles
} from '#material-ui/core/styles'
import {
CartContext
} from '../../contexts'
import {
addToCart,
removeFromCart
} from '../../contexts/actions'
import Cart from '../../images/Cart'
const useStyles = makeStyles(theme => ({
qtyText: {
color: ({
white
}) => (white ? theme.palette.secondary.main : '#fff'),
},
mainGroup: {
height: '3rem',
},
editButtons: {
height: '1.525rem',
borderRadius: 0,
backgroundColor: ({
white
}) =>
white ? '#fff' : theme.palette.secondary.main,
borderLeft: ({
white
}) =>
`2px solid ${white ? theme.palette.secondary.main : '#fff'}`,
borderRight: ({
round
}) => (round ? 0 : '2px solid #fff'),
borderBottom: 'none',
borderTop: 'none',
borderRadius: ({
round
}) => (round ? '0px 50px 50px 0px' : 0),
'&:hover': {
backgroundColor: ({
white
}) =>
white ? '#fff' : theme.palette.secondary.light,
},
},
endButtons: {
backgroundColor: ({
white
}) =>
white ? '#fff' : theme.palette.secondary.main,
borderRadius: 50,
border: 'none',
},
cartButton: {
marginLeft: '0 !important',
transition: 'background-color 1s ease',
},
minus: {
marginTop: '-0.25rem',
},
minusButton: {
borderTop: ({
white
}) =>
`2px solid ${white ? theme.palette.secondary.main : '#fff'}`,
},
qtyButton: {
'&:hover': {
backgroundColor: ({
white
}) =>
white ? '#fff' : theme.palette.secondary.main,
},
},
badge: {
color: '#fff',
fontSize: '1.5rem',
backgroundColor: theme.palette.secondary.main,
padding: 0,
},
success: {
backgroundColor: theme.palette.success.main,
'&:hover': {
backgroundColor: theme.palette.success.main,
},
},
}))
export default function QtyButton({
stock,
variants,
selectedVariant,
name,
isCart,
white,
hideCartButton,
round,
override,
}) {
const {
cart,
dispatchCart
} = useContext(CartContext)
const existingItem = isCart ?
cart.find(item => item.variant === variants[selectedVariant]) :
null
const classes = useStyles({
white,
round
})
const [qty, setQtyState] = useState(isCart ? existingItem.qty : 1)
const [success, setSuccess] = useState(false)
let setQty
if (override) {
setQty = val => {
override.setValue(val)
setQtyState(val)
}
} else {
setQty = setQtyState
}
const handleChange = direction => {
if (qty === stock[selectedVariant].qty && direction === 'up') {
return null
}
if (qty === 1 && direction === 'down') {
return null
}
const newQty = direction === 'up' ? qty + 1 : qty - 1
setQty(newQty)
if (isCart) {
if (direction === 'up') {
dispatchCart(addToCart(variants[selectedVariant], 1, name))
} else if (direction === 'down') {
dispatchCart(removeFromCart(variants[selectedVariant], 1))
}
}
}
const handleCart = () => {
setSuccess(true)
dispatchCart(
addToCart(
variants[selectedVariant],
qty,
name,
stock[selectedVariant].qty
)
)
}
useEffect(() => {
if (stock === null || stock === -1) {
return undefined
}
if (qty === 0 && stock[selectedVariant].qty !== 0) {
setQty(1)
} else if (qty > stock[selectedVariant].qty) {
setQty(stock[selectedVariant].qty)
}
}, [stock, selectedVariant])
useEffect(() => {
let timer
if (success) {
timer = setTimeout(() => setSuccess(false), 1500)
}
return () => clearTimeout(timer)
}, [success])
return ( <
Grid item >
<
ButtonGroup classes = {
{
root: classes.mainGroup
}
} >
<
Button classes = {
{
root: clsx(classes.endButtons, classes.qtyButton)
}
} >
<
Typography variant = "h3"
classes = {
{
root: classes.qtyText
}
} > {
qty
} <
/Typography> <
/Button> <
ButtonGroup orientation = "vertical" >
<
Button onClick = {
() => handleChange('up')
}
classes = {
{
root: classes.editButtons
}
} >
<
Typography variant = "h3"
classes = {
{
root: classes.qtyText
}
} >
+
<
/Typography> <
/Button> <
Button onClick = {
() => handleChange('down')
}
classes = {
{
root: clsx(classes.editButtons, classes.minusButton)
}
} >
<
Typography variant = "h3"
classes = {
{
root: clsx(classes.qtyText, classes.minus)
}
} >
-
<
/Typography> <
/Button> <
/ButtonGroup> {
hideCartButton ? null : ( <
Button onClick = {
handleCart
}
disabled = {
stock ? stock[selectedVariant].qty === 0 : true
}
classes = {
{
root: clsx(classes.endButtons, classes.cartButton, {
[classes.success]: success,
}),
}
} >
{
success ? ( <
Typography variant = "h3"
classes = {
{
root: classes.qtyText
}
} > ✓
<
/Typography>
) : ( <
Badge overlap = "circle"
badgeContent = "+"
classes = {
{
badge: classes.badge
}
} >
<
Cart color = "#fff" / >
<
/Badge>
)
} <
/Button>
)
} <
/ButtonGroup> <
/Grid>
)
}
It's impossible to follow this code, you've pasted images rather than code blocks so it's extremely hard to know where each part belongs (even more if they are parts repeated between snapshots). Try to debug and add the interesting parts, not the whole file...
and when i close out of the error I am left with this
Well, you should never close the issue. It needs to be fixed, not ignored.
Your gatsby-node.js, among querying a bunch of fields you are not using (image, price, etc) looks fair good. Remember that the gatsby-node.js is where you create pages and send data to your templates, just as is, don't query unnecessary fields to avoid long build times. You should query for those fields in your template query.
Said that regarding the "first issue", if your product page is not rendering, your problem is there. Run gatsby clean in each trial.
Regarding your blocking issue, the one is breaking your code, as I said, it's impossible to follow the trace given the details you've provided but I would try something like:
<Button
onClick={handleCart}
disabled={stock[selectedVariant] ? stock[selectedVariant].qty === 0 : true}
classes={{
root: clsx(classes.endButtons, classes.cartButton, {
[classes.success]: success,
}),
}}
>
Clearly, in some of your products you don't have a qty property inside the stock object, that's why your code is breaking. You should pull the thread to know if that product should or shouldn't have qty and adapt the code logic to that business logic, not otherwise. The snippet above should fix your code-breaking issue but you need to know if your Button should or shouldn't have qty at that point.
If you are using optional chaining plugin you can simplify it as:
disabled={stock?.selectedVariant?.qty === 0 : true}
I am using react-input-trigger library https://github.com/abinavseelan/react-input-trigger, to implement #mentions functionality (like twitter).
What I want to happen is for the list of available options to display when '#' is pressed.
What is currently happening is that I have to type '#' plus a character before the list will display. It is so close to what I need but I can't for the life of me figure out this part...!
This is my first question on stack overflow and I tried really hard to find an answer to this question but I couldn't find anything that solved this particular issue, apologies if I missed something.
My current code looks like this:
import React, { Component } from 'react';
import InputTrigger from 'react-input-trigger';
import { TextArea, Dropdown, Step, H3 } from "../../styled-components/styled-components"
class CreateMessage extends Component {
state = {
top: null,
left: null,
showSuggestor: false,
startPosition: null,
text: null,
currentSelection: 0,
textareaValue: "",
properties: [{name: "property1", type: "string"}, {name: "property2", type: "string"}, {name: "property3", type: "string"}, {name: "property4", type: "string"}]
}
toggleSuggestor = (metaInformation) => {
const { hookType, cursor } = metaInformation;
if (hookType === 'start') {
this.setState({
showSuggestor: true,
left: cursor.left,
top: cursor.top + 20,
startPosition: cursor.selectionStart,
});
}
if (hookType === 'cancel') {
this.setState({
showSuggestor: false,
left: null,
top: null,
text: null,
startPosition: null,
});
}
}
handleInput = (metaInformation) => {
this.setState({ text: metaInformation.text });
}
handleKeyDown = (event) => {
const { which } = event;
const { currentSelection } = this.state;
const keyInputs = {
down: 40,
up: 38,
enter: 13
}
let properties = this.state.properties.map(( p ) => p)
if (which === keyInputs.down ) {
event.preventDefault();
this.setState({
currentSelection: (currentSelection + 1) % properties.length,
});
}
if (which === keyInputs.up ) { // 40 is the character code of the up arrow
event.preventDefault();
this.setState({
currentSelection: (currentSelection - 1) % properties.length,
});
}
if (which === keyInputs.enter) { // 13 is the character code for enter
event.preventDefault();
const { currentSelection, startPosition, textareaValue } = this.state;
const property = properties[currentSelection];
const newText = `${textareaValue.slice(0, startPosition - 1)}{ ${property.name} }${textareaValue.slice(startPosition + property.name.length, textareaValue.length)}`
this.setState({
showSuggestor: false,
left: null,
top: null,
text: null,
startPosition: null,
textareaValue: newText,
});
this.endHandler();
}
}
handleTextareaInput = (event) => {
const { value } = event.target;
this.setState({ textareaValue: value })
}
render() {
let properties = this.state.properties.map(( p ) => p )
return (
<Step>
<H3>Enter Message:</H3>
<div style={{ position: 'relative' }} id="message-container" onKeyDown={this.handleKeyDown}>
<InputTrigger
trigger={{ keyCode: 50, shiftKey: true }}
onStart={(metaData) => { this.toggleSuggestor(metaData); }}
onCancel={(metaData) => { this.toggleSuggestor(metaData); }}
onType={(metaData) => { this.handleInput(metaData); }}
endTrigger={(endHandler) => { this.endHandler = endHandler; }}
>
<TextArea id="event-message" onChange={this.handleTextareaInput} value={this.state.textareaValue} />
</InputTrigger>
<Dropdown id="dropdown" style={{ display: this.state.showSuggestor ? "block" : "none", top: this.state.top, left: this.state.left }}>
{
properties
.filter(property => property.name.indexOf(this.state.text) !== -1)
.map((property, index) => (
<div
key={index}
className='dropdown-item'
style={{ padding: '10px 20px', background: index === this.state.currentSelection ? '#eee' : '' }}
>
{ property.name }
</div>
))
}
</Dropdown>
</div>
</Step>
);
}
}
export default CreateMessage;
I use expanded rows in fixed-data-table-2 component. I have 3 levels on inner tables:
If I click collapse cells in inner table (second nested level), rows don't expand and last nested table is not rendered. It occurs after first click of parent row, but after second click the table is rendered.
What is more strange, this behaviour doesn't happen if
a) I click first three rows of second table or
b) if I expand first row in main(first) table
It happens with last rows of second table if other than first row of main table is expanded.
You can see this behaviour in this and this recording.
codesandbox
CollapseCell.jsx
import React from 'react';
import { Cell } from 'fixed-data-table-2';
const { StyleSheet, css } = require('aphrodite');
export default class CollapseCell extends React.PureComponent {
render() {
const {
data, rowIndex, columnKey, collapsedRows, callback, ...props
} = this.props;
return (
<Cell {...props} className={css(styles.collapseCell)}>
<a onClick={evt => callback(rowIndex)}>{collapsedRows.has(rowIndex) ? '\u25BC' : '\u25BA'}</a>
</Cell>
);
}
}
const styles = StyleSheet.create({
collapseCell: {
cursor: 'pointer',
},
});
TestMeet.jsx
import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import isEmpty from 'lodash/isEmpty';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell.jsx';
import SecondInnerTable from './SecondInnerTable';
const { StyleSheet, css } = require('aphrodite');
export default class TestMeetView extends Component {
static propTypes = {};
state = {
tableData: [
{
start: '5/19',
end: '5/20',
host: 'DACA',
},
{
start: '6/15',
end: '6/15',
host: 'DACA',
},
{
start: '6/16',
end: '6/17',
host: 'DACA',
},
{
start: '7/15',
end: '7/16',
host: 'DACA',
},
{
start: '7/30',
end: '7/31',
host: 'DACA',
},
],
columnWidths: {
start: 200,
end: 200,
host: 200,
action: 100,
},
tableContainerWidth: 0,
numOfExpRows: 0,
expChildRows: {},
collapsedRows: new Set(),
scrollToRow: null,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ columnWidths }) => ({
columnWidths: {
...columnWidths,
[columnKey]: newColumnWidth,
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth;
this.setState({
tableContainerWidth: newTableContainerWidth,
columnWidths: {
start: newTableContainerWidth / 3,
end: newTableContainerWidth / 3,
host: newTableContainerWidth / 3,
},
});
}
};
handleCollapseClick = (rowIndex) => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
let resetExpChildRow = -1;
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
resetExpChildRow = rowIndex;
} else {
numOfExpRows += 1;
}
if (resetExpChildRow === -1) {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
});
} else {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
expChildRows: {
...this.state.expChildRows,
[rowIndex]: 0,
},
});
}
};
subRowHeightGetter = (index) => {
const numExpChildRows = this.state.expChildRows[index] ? this.state.expChildRows[index] : 0;
return this.state.collapsedRows.has(index) ? 242 * (numExpChildRows + 1) + 50 : 0;
};
rowExpandedGetter = ({ rowIndex, width, height }) => {
if (!this.state.collapsedRows.has(rowIndex)) {
return null;
}
const style = {
height,
width: width - 10,
};
return (
<div style={style}>
<div className={css(styles.expandStyles)}>
<SecondInnerTable
changeNumOfExpandedRows={this.setNumOfInnerExpandedRows}
parentRowIndex={rowIndex}
/>
</div>
</div>
);
};
setNumOfInnerExpandedRows = (numOfExpandedRows, rowIndex) => {
this.setState({
expChildRows: {
...this.state.expChildRows,
[rowIndex]: numOfExpandedRows,
},
});
};
render() {
let sumOfExpChildRows = 0;
if (!isEmpty(this.state.expChildRows)) {
sumOfExpChildRows = Object.values(this.state.expChildRows).reduce((a, b) => a + b);
}
return (
<div className="test-view">
<div className="container-fluid">
<div className="mb-5" ref={el => (this.tableContainer = el)}>
<Table
scrollToRow={this.state.scrollToRow}
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={40}
width={this.state.tableContainerWidth}
height={(this.state.numOfExpRows + sumOfExpChildRows + 1) * 242}
subRowHeightGetter={this.subRowHeightGetter}
rowExpanded={this.rowExpandedGetter}
touchScrollEnabled
onColumnResizeEndCallback={this.onTableColumnResizeEndCallback}
isColumnResizing={false}
>
<Column
cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
fixed
width={30}
/>
<Column
columnKey="start"
header={<Cell>Start</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].start}</Cell>}
width={this.state.columnWidths.start}
isResizable
/>
<Column
columnKey="end"
header={<Cell>End</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].end}</Cell>}
width={this.state.columnWidths.end}
isResizable
/>
<Column
columnKey="host"
header={<Cell>Host</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex].host}</Cell>}
width={this.state.columnWidths.host}
isResizable
/>
</Table>
</div>
</div>
</div>
);
}
}
const styles = StyleSheet.create({
expandStyles: {
height: '242px',
margin: '10px',
},
});
SecondInnerTable.jsx
import React, { Component } from 'react';
import { Table, Column, Cell } from 'fixed-data-table-2';
import debounce from 'lodash/debounce';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
import CollapseCell from './CollapseCell';
import ThirdInnerTable from './ThirdInnerTable';
const { StyleSheet, css } = require('aphrodite');
export default class SecondInnerTable extends Component {
state = {
tableData: [
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
{
dateOfSession: '5/19/18',
timeline: '4h00m/4h30m',
entries: '400/900',
},
],
tableColumns: {
dateOfSession: { label: 'Date of Session', width: 150 },
timeline: { label: 'Timeline', width: 150 },
entries: { label: 'Entries', width: 150 },
},
tableContainerWidth: 0,
tableContainerHeight: 252,
collapsedRows: new Set(),
scrollToRow: null,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onSessionsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ tableColumns }) => ({
tableColumns: {
...tableColumns,
[columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth - 20;
const newColumnsWidth = newTableContainerWidth / 3;
this.setState(({ tableColumns }) => ({
tableContainerWidth: newTableContainerWidth,
tableColumns: {
dateOfSession: { label: tableColumns.dateOfSession.label, width: newColumnsWidth },
timeline: { label: tableColumns.timeline.label, width: newColumnsWidth },
entries: { label: tableColumns.entries.label, width: newColumnsWidth },
},
}));
}
};
handleCollapseClick = (rowIndex) => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
} else {
numOfExpRows += 1;
}
this.setState(
{
tableContainerHeight: 252 * (numOfExpRows + 1),
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
},
() => {
this.props.changeNumOfExpandedRows(numOfExpRows, this.props.parentRowIndex);
},
);
};
subRowHeightGetter = index => (this.state.collapsedRows.has(index) ? 272 : 0);
rowExpandedGetter = ({ rowIndex, width, height }) => {
if (!this.state.collapsedRows.has(rowIndex)) {
return null;
}
const style = {
height,
width: width - 10,
};
return (
<div style={style}>
<div className={css(styles.expandStyles)}>
<ThirdInnerTable parentRowIndex={rowIndex} />
</div>
</div>
);
};
render() {
return (
<div className="mb-2" ref={el => (this.tableContainer = el)}>
<Table
scrollToRow={this.state.scrollToRow}
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={50}
width={this.state.tableContainerWidth}
height={this.state.tableContainerHeight}
subRowHeightGetter={this.subRowHeightGetter}
rowExpanded={this.rowExpandedGetter}
touchScrollEnabled
onColumnResizeEndCallback={this.onSessionsTableColumnResizeEndCallback}
isColumnResizing={false}
>
<Column
cell={<CollapseCell callback={this.handleCollapseClick} collapsedRows={this.state.collapsedRows} />}
fixed
width={30}
/>
{Object.keys(this.state.tableColumns).map(key => (
<Column
key={key}
columnKey={key}
header={<Cell>{this.state.tableColumns[key].label}</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
width={this.state.tableColumns[key].width}
isResizable
/>
))}
</Table>
</div>
);
}
}
const styles = StyleSheet.create({
expandStyles: {
height: '252px',
margin: '10px',
},
});
ThirdInnerTable.jsx
import React, { Component } from 'react';
import debounce from 'lodash/debounce';
import { Table, Column, Cell } from 'fixed-data-table-2';
import 'fixed-data-table-2/dist/fixed-data-table.min.css';
export default class ThirdInnerTable extends Component {
state = {
tableData: [
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
{
eventNumber: '1',
qualifyingTime: 'N/A',
selected: 'N/A',
},
],
tableColumns: {
eventNumber: { label: 'Event number', width: 150 },
qualifyingTime: { label: 'Qualifying time', width: 150 },
selected: { label: 'Selected?', width: 150 },
},
tableContainerWidth: 0,
numOfColumns: 3,
};
componentDidMount() {
this.updateWidth();
this.updateWidth = debounce(this.updateWidth, 200);
window.addEventListener('resize', this.updateWidth);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updateWidth);
}
onEventsTableColumnResizeEndCallback = (newColumnWidth, columnKey) => {
this.setState(({ tableColumns }) => ({
tableColumns: {
...tableColumns,
[columnKey]: { label: tableColumns[columnKey].label, width: newColumnWidth },
},
}));
};
updateWidth = () => {
if (this.tableContainer.offsetWidth === this.state.tableContainerWidth) {
return;
}
if (
this.tableContainer &&
this.tableContainer.offsetWidth !== this.state.tableContainerWidth
) {
const newTableContainerWidth = this.tableContainer.offsetWidth;
const columnWidth = newTableContainerWidth / 3;
this.setState(({ tableColumns }) => ({
tableContainerWidth: newTableContainerWidth,
tableColumns: {
eventNumber: { label: tableColumns.eventNumber.label, width: columnWidth },
qualifyingTime: { label: tableColumns.qualifyingTime.label, width: columnWidth },
selected: { label: tableColumns.selected.label, width: columnWidth },
},
}));
}
};
render() {
return (
<div className="mb-5" ref={el => (this.tableContainer = el)}>
<Table
rowsCount={this.state.tableData.length}
rowHeight={40}
headerHeight={50}
width={this.state.tableContainerWidth}
height={252}
touchScrollEnabled
onColumnResizeEndCallback={this.onEventsTableColumnResizeEndCallback}
isColumnResizing={false}
>
{Object.keys(this.state.tableColumns).slice(0, this.state.numOfColumns).map(key => (
<Column
key={key}
columnKey={key}
header={<Cell>{this.state.tableColumns[key].label}</Cell>}
cell={props => <Cell {...props}>{this.state.tableData[props.rowIndex][key]}</Cell>}
width={this.state.tableColumns[key].width}
isResizable
/>
))}
</Table>
</div>
);
}
}
I have updated the Sandbox code, Kindly check the below link, I think it is useful for you
Code Sandbox
Or
handleCollapseClick = rowIndex => {
const { collapsedRows } = this.state;
const shallowCopyOfCollapsedRows = new Set([...collapsedRows]);
let scrollToRow = rowIndex;
if (shallowCopyOfCollapsedRows.has(rowIndex)) {
shallowCopyOfCollapsedRows.delete(rowIndex);
scrollToRow = null;
} else {
shallowCopyOfCollapsedRows.add(rowIndex);
}
let numOfExpRows = 0;
if (collapsedRows.size > 0) {
numOfExpRows = collapsedRows.size;
}
let resetExpChildRow = -1;
if (collapsedRows.has(rowIndex)) {
numOfExpRows -= 1;
resetExpChildRow = rowIndex;
} else {
numOfExpRows += 1;
}
if (resetExpChildRow === -1) {
this.setState({
tableContainerHeight: 250 * numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows
});
} else {
this.setState({
numOfExpRows,
scrollToRow,
collapsedRows: shallowCopyOfCollapsedRows,
expChildRows: {
...this.state.expChildRows,
[rowIndex]: 0
}
});
}
};
I should have commented, but I don't have the required no. of points for that. Have you fixed the issue? Because I just opened the link you have attached and it's working fine
UPDATE
Your code works fine. The problem with your approach is that your click event is triggered on the arrow and not on your entire div (or cell). Therefore, you need to be on point and only click on the arrow in order for your row to expand. If you don't want this behavior, I'd suggest putting your click event on the div (or cell)
Here is a working video that demonstrates that it's working fine:
https://www.useloom.com/share/2c725f3fede942b09c661a765c72634e
I have taken a screenshot from your video which shows that because you're not clicking on the arrow, that's why your row is not expanding
I have this video, playing in zindex: -1 with a button and a text input floating over it. The issue is when the text changes, it's supposed to manipulate that state object, not fire the touchable highlight's on click function.
When I use the suggestion given yesterday, the error turns into a warning. If I type 7 random letters in the input box, I'll get 7 warnings saying: "warning bind() you are binding a component method to the component", which means the input box is continuing to call the touchable highlight's function.
I'm using this library for React Native to use it's streaming capabilities: https://github.com/oney/react-native-webrtc. It's pretty nice!
On one of it's examples, https://github.com/oney/RCTWebRTCDemo/blob/master/main.js there are these lines of code I'm fiddling with:
_renderTextRoom() {
return (
<View style={styles.listViewContainer}>
<ListView
dataSource={this.ds.cloneWithRows(this.state.textRoomData)}
enableEmptySections={true}
renderRow={rowData =>
<Text
style={styles.whiteOut}
>{`${rowData.user}: ${rowData.message}`}</Text>}
/>
<TextInput
style={[styles.whiteOut, styles.bgWhite]}
onChangeText={value => this.setState({ textRoomValue: value })}
value={this.state.textRoomValue}
/>
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={this._textRoomPress()}>
<Text style={styles.bgWhite}>Send</Text>
</TouchableHighlight>
</View>
</View>
);
},
When I enter text into the text field, the this._textRoomPress() function nested within the TouchableHighlight is firing. What!? When I comment it out, it doesn't fire.
'use strict';
import React, { Component } from 'react';
import {
Dimensions,
StyleSheet,
Text,
TouchableHighlight,
View,
TextInput,
ListView,
ScrollView
} from 'react-native';
import { userData } from '../utils/Factory';
import io from 'socket.io-client';
var socket_one = 'https://xxxxxxxxxxxxxx.herokuapp.com';
const socket = io.connect(socket_one, { transports: ['websocket'] });
import {
RTCPeerConnection,
RTCMediaStream,
RTCIceCandidate,
RTCSessionDescription,
RTCView,
MediaStreamTrack,
getUserMedia,
} from 'react-native-webrtc';
const configuration = { "iceServers": [{ "url": "stun:stun.l.google.com:19302" }] };
const pcPeers = {};
let localStream;
var width = Dimensions.get('window').width; //full width
var height = Dimensions.get('window').height; //full height
function getLocalStream(isFront, callback) {
MediaStreamTrack.getSources(sourceInfos => {
console.log(sourceInfos);
let videoSourceId;
for (const i = 0; i < sourceInfos.length; i++) {
const sourceInfo = sourceInfos[i];
if (sourceInfo.kind == "video" && sourceInfo.facing == (isFront ? "front" : "back")) {
videoSourceId = sourceInfo.id;
}
}
getUserMedia({
audio: true,
video: {
mandatory: {
minWidth: 700, // Provide your own width, height and frame rate here
minHeight: 700,
minFrameRate: 30
},
facingMode: (isFront ? "user" : "environment"),
optional: [{ sourceId: sourceInfos.id }]
}
}, function(stream) {
console.log('dddd', stream);
callback(stream);
}, logError);
});
}
function join(roomID) {
socket.emit('join', roomID, function(socketIds) {
console.log('join', socketIds);
for (const i in socketIds) {
const socketId = socketIds[i];
createPC(socketId, true);
}
});
}
function createPC(socketId, isOffer) {
const pc = new RTCPeerConnection(configuration);
pcPeers[socketId] = pc;
pc.onicecandidate = function(event) {
// console.warn('onicecandidate', event.candidate);
if (event.candidate) {
socket.emit('exchange', { 'to': socketId, 'candidate': event.candidate });
}
};
function createOffer() {
pc.createOffer(function(desc) {
console.log('createOffer', desc);
pc.setLocalDescription(desc, function() {
console.log('setLocalDescription', pc.localDescription);
socket.emit('exchange', { 'to': socketId, 'sdp': pc.localDescription });
}, logError);
}, logError);
}
pc.onnegotiationneeded = function() {
console.log('onnegotiationneeded');
if (isOffer) {
createOffer();
}
}
pc.oniceconnectionstatechange = function(event) {
console.log('oniceconnectionstatechange', event.target.iceConnectionState);
if (event.target.iceConnectionState === 'completed') {
setTimeout(() => {
getStats();
}, 1000);
}
if (event.target.iceConnectionState === 'connected') {
createDataChannel();
}
};
pc.onsignalingstatechange = function(event) {
console.log('onsignalingstatechange', event.target.signalingState);
};
pc.onaddstream = function(event) {
console.log('onaddstream', event.stream);
// container.setState({ info: 'One peer join!' });
container.setState({ info: 'Connected!' });
const remoteList = container.state.remoteList;
remoteList[socketId] = event.stream.toURL();
container.setState({ remoteList: remoteList });
};
pc.onremovestream = function(event) {
console.log('onremovestream', event.stream);
};
pc.addStream(localStream);
function createDataChannel() {
if (pc.textDataChannel) {
return;
}
const dataChannel = pc.createDataChannel("text");
dataChannel.onerror = function(error) {
console.log("dataChannel.onerror", error);
};
dataChannel.onmessage = function(event) {
console.log("dataChannel.onmessage:", event.data);
container.receiveTextData({ user: socketId, message: event.data });
};
dataChannel.onopen = function() {
console.log('dataChannel.onopen');
container.setState({ textRoomConnected: true });
};
dataChannel.onclose = function() {
console.log("dataChannel.onclose");
};
pc.textDataChannel = dataChannel;
}
return pc;
}
function exchange(data) {
const fromId = data.from;
let pc;
if (fromId in pcPeers) {
pc = pcPeers[fromId];
} else {
pc = createPC(fromId, false);
}
if (data.sdp) {
console.log('exchange sdp', data);
pc.setRemoteDescription(new RTCSessionDescription(data.sdp), function() {
if (pc.remoteDescription.type == "offer")
pc.createAnswer(function(desc) {
console.log('createAnswer', desc);
pc.setLocalDescription(desc, function() {
console.log('setLocalDescription', pc.localDescription);
socket.emit('exchange', { 'to': fromId, 'sdp': pc.localDescription });
}, logError);
}, logError);
}, logError);
} else {
console.log('exchange candidate', data);
pc.addIceCandidate(new RTCIceCandidate(data.candidate));
}
}
function leave(socketId) {
console.log('leave', socketId);
const pc = pcPeers[socketId];
const viewIndex = pc.viewIndex;
pc.close();
delete pcPeers[socketId];
const remoteList = container.state.remoteList;
delete remoteList[socketId]
container.setState({ remoteList: remoteList });
container.setState({ info: 'One peer leave!' });
}
socket.on('exchange', function(data) {
exchange(data);
});
socket.on('leave', function(socketId) {
leave(socketId);
});
socket.on('connect', function(data) {
console.log('connected');
});
function initStream() {
getLocalStream(true, function(stream) {
localStream = stream;
container.setState({ selfViewSrc: stream.toURL() });
// container.setState({ status: 'ready', info: 'Please enter or create room ID' });
container.setState({ status: 'connect', info: 'Connecting' });
if (userData.inDanger) {
join(0);
} else {
join(userData.userName);
// join(userData.nowPlaying);
}
});
}
function logError(error) {
console.log("logError", error);
}
function mapHash(hash, func) {
const array = [];
for (const key in hash) {
const obj = hash[key];
array.push(func(obj, key));
}
return array;
}
function _textRoomPress() {
if (!container.textRoomValue) {
return
}
const textRoomData = container.textRoomData.slice();
textRoomData.push({ user: 'Me', message: container.textRoomValue });
for (const key in pcPeers) {
const pc = pcPeers[key];
pc.textDataChannel.send(container.textRoomValue);
}
container.setState({ textRoomData, textRoomValue: '' });
}
function getStats() {
const pc = pcPeers[Object.keys(pcPeers)[0]];
if (pc.getRemoteStreams()[0] && pc.getRemoteStreams()[0].getAudioTracks()[0]) {
const track = pc.getRemoteStreams()[0].getAudioTracks()[0];
console.log('track', track);
pc.getStats(track, function(report) {
console.log('getStats report', report);
}, logError);
}
}
let container;
const Stream = React.createClass({
getInitialState: function() {
this.ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => true });
return {
info: 'Initializing',
status: 'init',
roomID: '',
// isFront: true,
isFront: false,
selfViewSrc: null,
remoteList: {},
textRoomConnected: false,
textRoomData: [],
textRoomValue: '',
};
},
componentDidMount: function() {
container = this;
initStream();
},
_press(event) {
// this.refs.roomID.blur();
this.setState({ status: 'connect', info: 'Connecting' });
join(userData.userName);
// join(this.state.roomID);
},
_switchVideoType() {
const isFront = !this.state.isFront;
this.setState({ isFront });
getLocalStream(isFront, function(stream) {
if (localStream) {
for (const id in pcPeers) {
const pc = pcPeers[id];
pc && pc.removeStream(localStream);
}
localStream.release();
}
localStream = stream;
container.setState({ selfViewSrc: stream.toURL() });
for (const id in pcPeers) {
const pc = pcPeers[id];
pc && pc.addStream(localStream);
}
});
},
receiveTextData(data) {
const textRoomData = this.state.textRoomData.slice();
textRoomData.push(data);
this.setState({ textRoomData, textRoomValue: '' });
},
_textRoomPress() {
if (!this.state.textRoomValue) {
return
}
const textRoomData = this.state.textRoomData.slice();
textRoomData.push({ user: 'Me', message: this.state.textRoomValue });
for (const key in pcPeers) {
const pc = pcPeers[key];
pc.textDataChannel.send(this.state.textRoomValue);
}
this.setState({ textRoomData, textRoomValue: '' });
},
_renderTextRoom() {
return (
<View style={styles.listViewContainer}>
<ListView
dataSource={this.ds.cloneWithRows(this.state.textRoomData)}
enableEmptySections={true}
renderRow={rowData =>
<Text
style={styles.whiteOut}
>{`${rowData.user}: ${rowData.message}`}</Text>}
/>
<TextInput
style={[styles.whiteOut, styles.bgWhite]}
onChangeText={value => this.setState({ textRoomValue: value })}
value={this.state.textRoomValue}
/>
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={this._textRoomPress()}>
<Text style={styles.bgWhite}>Send</Text>
</TouchableHighlight>
</View>
</View>
);
},
render() {
return (
<View style={styles.container}>
{
mapHash(this.state.remoteList, (remote, index) => {
return (
<ScrollView key={index}>
<RTCView key={index} streamURL={this.state.selfViewSrc} style={styles.remoteView}>
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={this._switchVideoType}>
<Text>Switch camera</Text>
</TouchableHighlight>
</View>
<View style={styles.bottomContainer}>
{this.state.textRoomConnected && this._renderTextRoom()}
</View>
</RTCView>
)
})
}
</View>
);
}
});
const styles = StyleSheet.create({
container: {
flex: 10,
// justifyContent: 'center',
backgroundColor: 'rgba(0,0,0, .0)',
},
topContainer: {
flex: 10,
backgroundColor: '#c7c7c7',
},
bottomContainer: {
flex: 1,
justifyContent: 'flex-end',
// backgroundColor: '#ffeeff',
'zIndex': 1,
backgroundColor: 'rgba(0,0,0, .0)',
},
selfView: {
width: 0,
height: 0
},
remoteView: {
flex: 1,
'zIndex': -1,
// backgroundColor: '#c7c7c7',
backgroundColor: '#f0f0f0',
width: width,
height: height - 25,
resizeMode: 'stretch', // or 'stretch'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
listViewContainer: {
height: 150,
},
buttonContainer: {
height: 50,
// backgroundColor: 'powderblue',
justifyContent: 'center',
alignItems: 'center',
},
button: {
marginTop: 50,
marginBottom: 50,
padding: 10,
paddingLeft: 30,
paddingRight: 30,
borderWidth: 1,
borderColor: 'rgba(0, 0, 0, .75)',
},
whiteOut: {
// color: "#ffffff",
color: "#000",
},
bgWhite: {
// backgroundColor: "#ffffff"
},
listView: {
// backgroundColor: "#ffffff",
flex: 10,
// flexDirection: 'row',
// justifyContent: 'center',
// alignItems: 'center',
}
});
export default Stream;
Replace it with this._textRoomPress.bind(this)
It is not firing arbitrarily, it is firing every time a render is issued. That happens because whatever you pass to an object as a prop is evaluated before being passed (as the parameters of a function are, basically), and so what you are passing is the returning value of the function, which of course is not what you want. By passing this._textRoomPress (with the optional bind in case that you want to keep the context of the object), you pass a reference to the function that will be later called by the component on the appropriate time (when the element is pressed).
Since you're using createClass and not the es6 syntax, all the methods are already autobinding to the Component. Simply just change your onPress to:
onPress={this._textRoomPress}>
If you use onPress={this._textRoomPress()}> It is instantly invoking that function anytime your component gets rendered.
In javascript you use <function name>() to invoke a function... What you are doing here is simply invoking that function every time that _renderTextRoom()gets called rather than assigning it to the onPress prop. What I would suggest is that you pass an anonymous function in as the prop (without calling it) which than returns the invocation of this._textRoomPress. ES6 arrow functions make this super easy because they do not bind their own this more info here
<View style={styles.buttonContainer}>
<TouchableHighlight
style={styles.button}
onPress={() => this._textRoomPress()}>
<Text style={styles.bgWhite}>Send</Text>
</TouchableHighlight>
</View>
I was having a problem with render another view in React Native.
Currently, I was trying to display another view(render another view) on click of a button. Here is my React Native code:
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
View,
Text,
ScrollView,
Image,
TextInput,
Button
} = React;
var Button = require('./node_modules/react-native-button');
var TemplateApp = React.createClass({
_handlePress: function(event) {
// CODE TO DISPLAY ANOTHER VIEW
},
render: function() {
return (
<View>
<Button style =
{{
fontSize: 20,
height: 40,
padding: 5,
margin: 10,
backgroundColor: 'black',
color: 'green'
}}
styleDisabled = {{color: 'red'}}
onPress = {
this._handlePress
}
>
Sign In
</Button>
</View>
);
}
});
var homeApp = React.createClass({
render: function() {
return ( < View >
< Text > Welcome Home < /Text> < /View>
)
}
})
AppRegistry.registerComponent('App', () => TemplateApp);
On click of the button, inside the _handlePress function I want to display the home view. Can anyone point how to do that?
You could solve it by using state and render different content accordingly. Something like this:
var TemplateApp = React.createClass({
getInitialState: function() {
return {buttonPressed: false};
},
_handlePress: function(event) {
this.setState({buttonPressed: true}
},
render: function() {
if (!this.state.buttonPressed) {
return ( < View >
< Button style = {
{
fontSize: 20,
height: 40,
padding: 5,
margin: 10,
backgroundColor: 'black',
color: 'green'
}
}
styleDisabled = {
{
color: 'red'
}
}
onPress = {
this._handlePress
} >
Sign In < /Button>
< /View>
);}
else {
return <HomeApp />
}
}
});
var HomeApp = React.createClass({
render: function() {
return ( < View >
< Text > Welcome Home < /Text> < /View>
)
}
})
AppRegistry.registerComponent('App', () => TemplateApp);