The html video element below does not play the webcam stream.
setState works fine since the browser notifies me that the site is accessing the camera.
It still invokes the webcam but the html video element is not activated after state change.
What i see is the black screen even if the webcam is active.
There are no error messages on browser console
any help appreciated
import React, {useState,useEffect} from 'react';
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import Paper from "#material-ui/core/Paper";
import { connect } from "react-redux";
const styles = () => ({
root: {
display: "flex",
flexDirection: "column",
width: "20%",
height: "25%",
overflowY: "auto",
},
videoPreview:{
alignSelf: "center",
width: "30%",
backgroundColor: "rgba(0, 0, 0, 0.25)",
marginTop: 20,
},
});
const Preview = (props) => {
const {classes} = props;
const [videoPreviewTrack, setVideoPreviewTrack] = useState(navigator.mediaDevices.getUserMedia({video:true}) );
useEffect(() => {
//something here maybe?
});
return (
<div className={classes.videoPreview}>
<video src={videoPreviewTrack} autoPlay={true} id={"videoPreviewElement"}>
</video ></div>
);
};
Preview.propTypes = {
classes: PropTypes.object.isRequired,
};
export default connect()(withStyles(styles)(Preview));
import React, {useState,useEffect,useRef} from 'react';
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import Paper from "#material-ui/core/Paper";
import { connect } from "react-redux";
import Select from '#material-ui/core/Select';
const styles = () => ({
root: {
display: "flex",
flexDirection: "column",
width: "20%",
height: "25%",
overflowY: "auto",
},
previewBackground:{
alignSelf: "center",
backgroundColor: "rgba(0, 0, 0, 0.25)",
marginTop: 20,
},
videoPreview:{
alignSelf: "center",
width: "15%",
backgroundColor: "rgba(0, 0, 0, 0.25)",
marginTop: 5,
},
});
const Preview = (props) => {
const {classes} = props;
const videoRef = useRef(null);
useEffect(() => {
getVideo();
}, [videoRef]);
const getVideo = () => {
navigator.mediaDevices
.getUserMedia( {video: true} )
.then(stream => {
let video = videoRef.current;
video.srcObject = stream;
video.play();
})
.catch(err => {
console.error("error:", err);
});
};
return (<div align={"center"} className={classes.previewBackground} >
<div >
<video className={classes.videoPreview} ref={videoRef} >
</video >
</div>
</div>
);
};
Preview.propTypes = {
classes: PropTypes.object.isRequired,
};
export default (withStyles(styles)(Preview));
Related
What I Need
I need a way to test if an accordion isCollapsed or not. I tried seeing if there was a way to grab if the maxHeight hasChanged, but from what I have read, it doesn't include measurements within the dom objects for the tests
The Problem
Writing the following test:
import React from 'react';
import { render, screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
import Accordion from './Accordion';
import AccordionItem from './AccordionItem';
const accordionTitle = 'Title: Hello World';
const accordionContent = 'Content: Hello World';
function TestContainer() {
return (
<Accordion>
<AccordionItem title={accordionTitle}>
<p>{accordionContent}</p>
</AccordionItem>
</Accordion>
);
}
describe('Accordion', () => {
const userViewing = userEvent.setup();
it.only('expands content on item control click', async () => {
render(<TestContainer />);
expect(await screen.findByText(accordionContent)).not.toBeInTheDocument();
const accordionItem = screen.getByRole('button', { name: accordionTitle });
userViewing.click(accordionItem);
expect(await screen.findByText(accordionContent)).toBeInTheDocument();
});
});
Results in the following Error:
The Reason
I think this is because the content component still exists in the DOM, but is only being hidden by the overflow: hidden; and maxHeight: 0 or heightOfContent(for animation purposes).
The Component Code
Accordion.tsx:
import React, { ReactNode } from 'react';
function Accordion({ children }: PROPS): JSX.Element {
return <div>{children}</div>;
}
interface PROPS {
children: ReactNode;
}
export default Accordion;
AccordionItem.tsx
import React, { ReactNode, useState } from 'react';
import AccordionControlClick from './AccordionControlClick';
import AccordionContent from './AccordionContent';
import { useStyles } from './Styles';
function AccordionItem({ title, children }: PROPS): JSX.Element {
const classes = useStyles();
const [isCollapsed, setIsCollapsed] = useState(true);
return (
<div className={isCollapsed ? classes.accordionItemClosed : classes.accordionItemOpen}>
<AccordionControlClick
title={title}
isCollapsed={isCollapsed}
toggleIsCollapsed={setIsCollapsed}
/>
{children !== null && (
<AccordionContent isCollapsed={isCollapsed}>{children}</AccordionContent>
)}
</div>
);
}
interface PROPS {
title: string;
children?: ReactNode;
}
export default AccordionItem;
AccordionControlClick.tsx
import React from 'react';
import { useStyles } from './Styles';
function AccordionControlClick({ title, isCollapsed, toggleIsCollapsed }: PROPS): JSX.Element {
const classes = useStyles();
return (
<button
className={classes.accordionControlClick}
type="button"
onClick={() => toggleIsCollapsed(!isCollapsed)}
>
<h2>{title}</h2>
<span className={isCollapsed ? classes.iconChevronWrapper : classes.iconChevronWrapperRotate}>
<i class="fa-solid fa-chevron-down" />
</span>
</button>
);
}
interface PROPS {
title: string;
isCollapsed: boolean;
toggleIsCollapsed: (isOpen: boolean) => void;
}
export default AccordionControlClick;
AccordionContent.tsx
import React, { ReactNode, useRef, useLayoutEffect } from 'react';
import { useStyles } from './Styles';
function AccordionContent({ isCollapsed, children }: PROPS): JSX.Element {
// variables
const componentDomRef = useRef<any>(null);
const componentHeight = useRef(0);
const classes = useStyles();
// setup
useLayoutEffect(() => {
componentHeight.current = componentDomRef.current ? componentDomRef.current.scrollHeight : 0;
}, []);
// render
return (
<div
ref={componentDomRef}
className={classes.accordionContent}
style={isCollapsed ? { maxHeight: '0px' } : { maxHeight: `${componentHeight.current}px` }}
>
{children}
</div>
);
}
interface PROPS {
isCollapsed: boolean;
children: ReactNode;
}
export default AccordionContent;
Styles.ts
import { createUseStyles } from 'react-jss';
import { cssColors, cssSpacing } from '../../utils';
const accordionBoxShadow = '2px 3px 8px 1px rgba(0, 0, 0, 0.2)';
const accordionBoxShadowTransition = 'box-shadow 0.3s ease-in-out 0s;';
export const useStyles = createUseStyles({
accordionContent: {
boxSizing: 'border-box',
width: '100%',
padding: `0 ${cssSpacing.m}`,
overflow: 'hidden',
transition: 'max-height 0.3s ease-in-out'
},
accordionControlClick: {
boxSizing: 'border-box',
display: 'flex',
width: '100%',
alignItems: 'center',
padding: `9px ${cssSpacing.m}`,
border: 'none',
borderRadius: '8px',
outline: 'none',
backgroundColor: `${cssColors.backgroundLevel2}`,
cursor: 'pointer'
},
accordionItemClosed: {
boxSizing: 'border-box',
width: '100%',
marginBottom: cssSpacing.l,
border: `2px solid ${cssColors.accordionTitleBorder}`,
borderRadius: '8px',
boxShadow: 'none',
transition: 'none',
'&:hover': {
border: `2px solid ${cssColors.accordionTitleBorder}`,
boxShadow: accordionBoxShadow,
transition: accordionBoxShadowTransition
}
},
accordionItemOpen: {
boxSizing: 'border-box',
width: '100%',
marginBottom: cssSpacing.l,
border: `2px solid ${cssColors.accordionTitleBorder}`,
borderRadius: '8px',
boxShadow: accordionBoxShadow,
transition: accordionBoxShadowTransition,
'&:hover': {
border: `2px solid ${cssColors.accordionTitleBorder}`,
boxShadow: accordionBoxShadow,
transition: accordionBoxShadowTransition
}
},
iconChevronWrapper: {
marginLeft: 'auto',
transform: 'none',
transition: 'transform 300ms ease'
},
iconChevronWrapperRotate: {
marginLeft: 'auto',
transform: 'rotate(180deg)',
transition: 'transform 300ms ease'
}
});
In the AccordionContent.tsx file, I added in the following visibility
style={ isCollapsed ? { maxHeight: '0px', visibility: 'hidden' } : { maxHeight: `${componentHeightRef.current}px`, visibility: 'visible' } }
In the test file I make the following changes to test if the content is ToBeVisible vs toBeInTheDocument:
it.only('expands content on item control click', async () => {
render(<TestContainerOneItem />);
const accordionContentContainer = (await screen.findByText(accordionContent)).parentElement;
await waitFor(() => expect(accordionContentContainer).not.toBeVisible());
const accordionControlButton = screen.getByRole('button', { name: accordionTitle });
await userViewing.click(accordionControlButton);
await waitFor(() => expect(accordionContentContainer).toBeVisible());
});
Before v5, I was able to apply multiple class to the component like the following:
import React from "react";
import { makeStyles } from '#mui/styles';
const useStyles = makeStyles({
image: {
borderRadius: "50%",
width: 256,
height: 256,
},
shadow: {
boxShadow: "-4px -3px 45px 21px rgba(0,0,0,0.35)",
},
}));
const MyComponent = (props) => {
const { data } = props;
const classes = useStyles();
return (
<img src={data.image} className={`${classes.image} ${classes.shadow}`} alt={data.title} />
);
};
export default MyComponent;
However, since the change of styling engine(?), it seems like I can only apply a single styling like:
import React from "react";
import { styled } from "#mui/system";
const ProfileImage = styled("img")({
borderRadius: "50%",
width: 256,
height: 256,
});
const MyComponent= (props) => {
const { data } = props;
return (
<ProfileImage src={data.image} alt={data.title} />
);
};
export default MyComponent;
Would there be any possible solution to achieve the same behavior as the one above?
Thank you!
You could use an object and just spread the properties you want to use
const classes = {
image: {
borderRadius: "50%",
width: 256,
height: 256
},
shadow: {
boxShadow: "-4px -3px 45px 21px rgba(0,0,0,0.35)"
}
};
const ProfileImage1 = styled("img")({
...classes.image,
...classes.shadow
});
If you are using JS and want to benefit from type intellisense, you could use type annotation to get the hint of CSS properties
import { styled, SxProps, Theme } from "#mui/system";
/** #type{Record<string, SxProps<Theme>>} */
const classes = {
image: {
borderRadius: "50%",
width: 256,
height: 256
},
shadow: {
boxShadow: "-4px -3px 45px 21px rgba(0,0,0,0.35)"
}
};
Also, if you want to make the class conditional, styled component allows you to use prop from the component
const ProfileImage1 = styled("img")(({ shadow }) => ({
...classes.image,
...(shadow ? classes.shadow : {})
}));
export default function App() {
return (
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
gap: 5
}}
>
<ProfileImage1 src={"https://via.placeholder.com/256"} shadow />
<ProfileImage1 src={"https://via.placeholder.com/256"} />
</Box>
);
}
I have a problem with add font in React with use Aphrodite.
In the Aphrodite documentation (https://github.com/Khan/aphrodite) we have described how to add our font to project, but it didn't work for me.
In terminal (vsc), after npm start I don't see any errors,
but in the browser (chrome) I have this: TypeError: _nested.set is not a function.
import React, { FC, useState } from "react";
import ScreenWrapper from "components/ScreenWrapper";
import StyledInput from "components/StyledInput";
import TextArea from "components/TextArea";
import StyledText from "components/StyledText";
import { StyleSheet, css } from "aphrodite";
import { colors } from "styles";
import { i18n } from "locale";
import "fonts/Roboto.woff";
interface Props {}
const HomeScreen: FC<Props> = () => {
const [inputValue1, setInputValue1] = useState<string>("");
const [textareaValue1, setTextareaValue1] = useState<string>("");
const handleInputValue = (value: string): void => {
setInputValue1(value);
};
const handleTextareaValue = (value: string): void => {
setTextareaValue1(value);
};
return (
<ScreenWrapper>
<div className={css(styles.homeScreen)}>
<h3>{i18n.t("header:title")}</h3>
<StyledInput
value={inputValue1}
onChange={handleInputValue}
placeholder={i18n.t("textAreaPlaceholder:search")}
/>
<TextArea
value={textareaValue1}
onChange={handleTextareaValue}
placeholder={i18n.t("textAreaPlaceholder:type")}
/>
<StyledText style={styles.testText}>{i18n.t("test:lorem")}</StyledText>
</div>
</ScreenWrapper>
);
};
const Roboto = {
fontFamily: "Roboto",
fontStyle: "normal",
fontWeight: "normal",
src: "url('fonts/Roboto.woff') format('woff')",
};
const styles = StyleSheet.create({
homeScreen: {
flexDirection: "column",
alignItems: "stretch",
display: "flex",
flexGrow: 1,
backgroundColor: colors.white,
textAlign: "center",
textTransform: "uppercase",
color: colors.blue4,
},
testText: {
margin: "20px 10vw",
fontFamily: Roboto,
},
});
export default HomeScreen;
Had anyone similar problem?
Try the following.
const styles = StyleSheet.create({
testText: {
margin: "20px 10vw",
fontFamily: [AldotheApacheFont, "sans-serif"],
},
});
Or check below https://www.npmjs.com/package/aphrodite-local-styles#font-faces
So I got the latest versions of jss and material ui.
Im using withStyles HOC and dynamically changing css in styles object, but I cant seem to be able to declare keyframes in css. Im also using nextjs if that makes any difference. Ive looked at how material-ui declares their animations, and I'm following their example https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/ButtonBase/TouchRipple.js.
import React, { useEffect, useState } from "react";
import classnames from "classnames";
import PropTypes from "prop-types";
import { withStyles, makeStyles } from "#material-ui/core/styles";
import { sectionAnchors, maxContentWidth } from "../../../util/constants";
import LearnMoreLink from "./LearnMoreLink";
import SectionContent from "../../common/SectionContent";
import content from "../../../../content";
import useRealHeight from "../../../util/useRealHeight";
import SplashHeading from "./SplashHeading";
import { singleHeight, doubleHeight } from "../../common/Footer";
import GetStartedForm from "./GetStartedForm";
import { withRouter } from "next/router";
import SwipeableTextMobileStepper from "./SwipeableTextMobileStepper";
import { Link } from 'next/router'
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormHelperText from '#material-ui/core/FormHelperText';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
import { green } from '#material-ui/core/colors';
import RadioButtonUncheckedIcon from '#material-ui/icons/RadioButtonUnchecked';
import RadioButtonCheckedIcon from '#material-ui/icons/RadioButtonChecked';
import Switch from '#material-ui/core/Switch';
import Button from '#material-ui/core/Button';
const styles = theme => ({
heading: {
marginBottom: theme.spacing.unit * 6
},
headingColored: {
marginBottom: 0,
color: theme.palette.primary.dark
},
container: {
width: "100%",
// '#keyframes fade': {
// '0%': {
// opacity: 1
// },
// '100%': {
// opacity: 0
// }
// },
'#keyframes enter': {
'0%': {
transform: 'scale(0)',
opacity: 0.1,
},
'100%': {
transform: 'scale(1)',
opacity: 0.3,
},
},
// backgroundColor: theme.palette.primary.dark,
// background: 'url(https://images.newscientist.com/wp-content/uploads/2019/04/08111018/screenshot-2019-04-08-10.24.34.jpg)',
backgroundImage: props => props.background,
backgroundSize: "cover",
// animation: '$fadeMe linear 1s infinite',
// animationName: '#fadeMe',
// animdationDuration: '1s',
// animation:
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center"
},
containerWizard: {
background: "none",
marginBottom: -doubleHeight,
paddingBottom: doubleHeight + theme.spacing.unit * 3,
[theme.breakpoints.up("sm")]: {
marginBottom: -singleHeight,
paddingBottom: singleHeight + theme.spacing.unit * 3
}
},
inner: {
maxWidth: maxContentWidth,
width: "100%",
flex: "1 0 auto",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
},
flex: {
flex: "1 0 auto"
},
form: {
width: "100%",
maxWidth: maxContentWidth / 2,
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center"
}
});
// http://www.coverbash.com/wp-content/covers/caeli_flowers_facebook_cover.jpg
function SplashLogic() {
const [selectedValue, setSelectedValue] = React.useState(1);
let int
useEffect(() => { int = init(1) }, []);
let background = selectedValue == 1 ? bG('http://www.coverbash.com/wp-content/covers/caeli_flowers_facebook_cover.jpg') :
bG('https://images.newscientist.com/wp-content/uploads/2019/04/08111018/screenshot-2019-04-08-10.24.34.jpg?')
const handleChange = event => {
setSelectedValue(event.target.value);
clearInterval(int)
int = init(event.target.value)
};
function init(num) {
return setInterval(() => {
background = bG('http://www.coverbash.com/wp-content/covers/caeli_flowers_facebook_cover.jpg')
setSelectedValue(2)
}, 4000)
}
return <SplashFull {...{ setSelectedValue, selectedValue, background, handleChange }}></SplashFull>
}
const SplashSection = ({ classes, router, setSelectedValue, selectedValue, background, handleChange }) => {
// const [height] = useRealHeight({ resize: false });
console.log(classes.container)
useEffect(() => {
router.prefetch("/signup");
}, []);
const onSubmit = values => {
router.push(`/signup?destination=${values.destination}`, "/signup");
};
return (
<SectionContent id={sectionAnchors.SPLASH} className={classes.container}>
<div className="bg-white opacity-50 rounded-full">
<Radio
checked={selectedValue === 1}
onChange={handleChange}
value={1}
name="radio-button-demo"
inputProps={{ 'aria-label': 'A' }}
/>
<Radio
checked={selectedValue === 2}
onChange={handleChange}
value={2}
name="radio-button-demo"
inputProps={{ 'aria-label': 'B' }}
/>
<Radio
checked={selectedValue === 3}
onChange={handleChange}
value={3}
name="radio-button-demo"
inputProps={{ 'aria-label': 'D' }}
/>
<Radio
checked={selectedValue === 4}
onChange={handleChange}
value={4}
name="radio-button-demo"
inputProps={{ 'aria-label': 'E' }}
/>
</div>
<div className={classes.inner}>
<SplashHeading
classes={{
container: classes.heading
}}
/>
<GetStartedForm className={classes.form} onSubmit={onSubmit} />
</div>
<Button variant="contained" >
Default
</Button>
<LearnMoreLink />
</SectionContent>
// <SwipeableTextMobileStepper></SwipeableTextMobileStepper>
);
};
SplashSection.propTypes = {
classes: PropTypes.object.isRequired
};
function bG(arg) {
return `url(${arg})`
}
const SplashFull = withStyles(styles)(withRouter(SplashSection));
export default SplashLogic
I get error:
container.addRule(...).addRule is not a function
TypeError: container.addRule(...).addRule is not a function
at Array.onProcessStyle (/workspace/travelcontacts/node_modules/jss-plugin-nested/dist/jss-plugin-nested.cjs.js:96:10)
at PluginsRegistry.onProcessStyle (/workspace/travelcontacts/node_modules/jss/dist/jss.cjs.js:1246:51)
at PluginsRegistry.onProcessRule (/workspace/travelcontacts/node_modules/jss/dist/jss.cjs.js:1235:26)
at Array.forEach (<anonymous>)
at RuleList.process (/workspace/travelcontacts/node_modules/jss/dist/jss.cjs.js:871:25)
at new StyleSheet (/workspace/travelcontacts/node_modules/jss/dist/jss.cjs.js:1041:16)
at Jss.createStyleSheet (/workspace/travelcontacts/node_modules/jss/dist/jss.cjs.js:2007:17)
at attach (/workspace/travelcontacts/node_modules/#material-ui/styles/makeStyles/makeStyles.js:116:39)
at /workspace/travelcontacts/node_modules/#material-ui/styles/makeStyles/makeStyles.js:256:7
at useSynchronousEffect (/workspace/travelcontacts/node_modules/#material-ui/styles/makeStyles/makeStyles.js:210:14)
at /workspace/travelcontacts/node_modules/#material-ui/styles/makeStyles/makeStyles.js:248:5
at Object.WithStyles [as render] (/workspace/travelcontacts/node_modules/#material-ui/styles/withStyles/withStyles.js:70:21)
at ReactDOMServerRenderer.render (/workspace/travelcontacts/node_modules/react-dom/cjs/react-dom-server.node.development.js:3758:44)
at ReactDOMServerRenderer.read (/workspace/travelcontacts/node_modules/react-dom/cjs/react-dom-server.node.development.js:3538:29)
at renderToString (/workspace/travelcontacts/node_modules/react-dom/cjs/react-dom-server.node.development.js:4247:27)
at render (/workspace/travelcontacts/node_modules/next-server/dist/server/render.js:86:16)
The issue appears to be resolved by not nesting #keyframes definitions. The hint was in the second application of the addRule function in the error: container.addRule(...).addRule is not a function.
Solution
Try moving your keyframe animations to the root level. So from this
const styles = theme => ({
container: {
'#keyframes enter': {
'0%': {
transform: 'scale(0)',
opacity: 0.1,
},
'100%': {
transform: 'scale(1)',
opacity: 0.3,
},
}
}
}
to this
const styles = theme => ({
'#keyframes enter': {
'0%': {
transform: 'scale(0)',
opacity: 0.1,
},
'100%': {
transform: 'scale(1)',
opacity: 0.3,
},
}
}
Hope that helps.
Interesting side note: nesting the animation another level removes the error, but does not instantiate the CSS animation.
from
const styles = theme => ({
container: {
'#keyframes enter': {
'0%': {
transform: 'scale(0)',
opacity: 0.1,
},
'100%': {
transform: 'scale(1)',
opacity: 0.3,
},
}
}
}
to
const styles = theme => ({
'#global':{ //need add into global rules
'#keyframes enter': {
'0%': {
transform: 'scale(0)',
opacity: 0.1,
},
'100%': {
transform: 'scale(1)',
opacity: 0.3,
},
}
}
}
I am trying to show the detail view when user taps on one of the list item. When the user taps on the listitem, I get an error saying Undefined is not an object
evaluating 'this.props.services.ser. Below is the screen shot of the error:
My list item page code is below:
import React, { Component } from 'react';
import { Text, View, StyleSheet, ListView } from 'react-native';
import { Provider, connect } from 'react-redux';
import { createStore } from 'redux'
import reducers from '../reducers/ServiceReducer';
import ServiceItem from './ServiceItem';
import Icon from 'react-native-vector-icons/EvilIcons';
import ServiceDetail from './ServiceDetail';
const styles = StyleSheet.create({
container: {
flex: 1,
width: 353,
flexWrap: 'wrap',
paddingTop: 20,
paddingLeft: 20,
},
});
const store = createStore(reducers);
class AutoCompActivity extends Component {
renderInitialView() {
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
});
this.dataSource = ds.cloneWithRows(this.props.services);
if (this.props.detailView === true) {
return (
<ServiceDetail />
);
} else {
return (
<ListView
enableEmptySections={true}
dataSource={this.dataSource}
renderRow={(rowData) =>
<ServiceItem services={rowData} />
}
/>
);
}
}
render() {
return (
<View style={styles.container}>
{this.renderInitialView()}
</View>
);
}
}
const mapStateToProps = state => {
return {
services: state.services,
detailView: state.detailView,
};
};
const ConnectedAutoCompActivity = connect(mapStateToProps)(AutoCompActivity);
const app1 = () => (
<Provider store={store}>
<ConnectedAutoCompActivity />
</Provider>
)
export default app1;
Each item that I display to the user is defined in js class serviceItem.js. Below is the code for serviceItem.js. I put the entire code inside TouchableWithoutFeedback. This is when I am getting an error when user taps on one of the listItem. I looked at this code several times, but could not figure out where I am doing wrong
import React from 'react';
import { Text, View, StyleSheet, Image, TouchableWithoutFeedback } from 'react-native';
import { connect } from 'react-redux';
import { getTheme } from 'react-native-material-kit';
import Icon from 'react-native-vector-icons/EvilIcons';
import * as actions from '../actions';
const theme = getTheme();
const styles = StyleSheet.create({
card: {
marginTop: 20,
},
title: {
top: 20,
left: 80,
fontSize: 24,
},
image: {
height: 100,
},
action: {
backgroundColor: 'black',
color: 'white',
},
icon: {
position: 'absolute',
top: 15,
left: 0,
color: 'white',
backgroundColor: 'rgba(255,255,255,0)',
},
});
const ServiceItem = (props) => {
return (
<TouchableWithoutFeedback
onPress={() => props.selectServices(props.services)}
>
<View style={[theme.cardStyle, styles.card]}>
<Text >{props.services.ser} </Text>
</View>
</TouchableWithoutFeedback>
);
};
const mapStateToProps = state => {
return {
selectServices: state.selectServices,
services: state.services
};
};
export default connect(mapStateToProps, actions)(ServiceItem);
When the user taps on each listitem. I am calling the class ServiceDetail.js. Below is the code for serviceDetail.js class:
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import { Text, View, StyleSheet, Image, ScrollView, TouchableOpacity, Linking } from 'react-native';
import { connect } from 'react-redux';
import { getTheme } from 'react-native-material-kit';
import EvilIcon from 'react-native-vector-icons/EvilIcons';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import SimpleIcon from 'react-native-vector-icons/SimpleLineIcons';
import * as actions from '../actions';
const theme = getTheme();
const styles = StyleSheet.create({
card: {
marginTop: 10,
paddingBottom: 20,
marginBottom: 20,
borderColor: 'lightgrey',
borderWidth: 0.5,
},
title1: {
top: 10,
left: 80,
fontSize: 24,
},
title2: {
top: 35,
left: 82,
fontSize: 18,
},
image: {
flex: 0,
height: 100,
width: 333,
backgroundColor: 'transparent',
justifyContent: 'center',
alignItems: 'center',
},
closeIcon: {
position: 'absolute',
top: 5,
left: 295,
color: 'rgba(233,166,154,0.8)',
backgroundColor: 'rgba(255,255,255,0)',
},
icon: {
position: 'absolute',
top: 15,
left: 0,
color: 'white',
backgroundColor: 'rgba(255,255,255,0)',
},
textArea: {
flexDirection: 'row',
paddingLeft: 20,
paddingTop: 10,
width: 260,
},
textIcons: {
color: '#26a69a',
},
actionArea: {
paddingTop: 10,
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
},
});
class ServiceDetail extends Component {
handleClick = (link) => {
Linking.canOpenURL(link).then(suppported => {
if (supported) {
Linking.openURL(link);
} else {
console.log('Don\'t know how to open URI: ' + link);
}
});
};
render() {
return (
<ScrollView showsVerticalScrollIndicator={false}>
<View style={[theme.cardStyle, styles.card]}>
<Image
source={require('../images/background.jpg')}
style={[theme.cardImageStyle, styles.image]}
/>
<EvilIcon name={'user'} size={100} style={styles.icon}/>
<SimpleIcon name={'close'} size={30} style={styles.closeIcon}
onPress={() => this.props.noneSelected()} />
<Text style={[theme.cardTitleStyle, styles.title1]}>{this.props.services.ser}</Text>
<Text style={[theme.cardTitleStyle, styles.title2]}>from {this.props.services.Location}</Text>
<Text style={[theme.cardTitleStyle, styles.title2]}>from {this.props.services.SecondLoc}</Text>
<View style={styles.textArea}>
<MaterialIcon name={'phone'} size={40} style={styles.textIcons}/>
<Text style={theme.cardContentStyle}>{this.props.services.Phone}</Text>
</View>
<View style={styles.textArea}>
<MaterialIcon name={'email'} size={40} style={styles.textIcons}/>
<Text style={theme.cardContentStyle}>{this.props.services.email}</Text>
</View>
<View style={styles.actionArea}>
<Text>Call</Text>
<Text>Email</Text>
</View>
</View>
</ScrollView>
);
}
}
const mapStateToProps = state => {
return {
service: state.serviceSelected,
};
};
export default connect(mapStateToProps, actions)(ServiceDetail);
My index.js class has the following code under actions folder:
export const selectServices = (serviceId) => {
return {
type: 'SELECTED_SERVICE',
payload: serviceId,
};
};
export const noneSelected = () => {
return {
type: 'NONE_SELECTED',
};
};
My serviceReducer has the following code:
import services from './services.json';
const initialState = {
services,
detailView: false,
serviceSelected: null,
};
export default (state = initialState, action) => {
switch (action.type) {
case 'SELECTED_SERVICE':
return {
...state,
detailView: true,
serviceSelected: action.payload
}
case 'NONE_SELECTED':
return {
...state,
detailView: false,
serviceSelected: null,
}
default:
return state;
}
}
I am developing this application on windows machine on android emulator. I know I am missing something. I looked at each and every line, but could not figure out what am I doing wrong. I am new to react native and trying to follow the example to make this app. I tried to debug the application using chrome, but keep getting an error that could not connect to remote debugging.
any help will be highly appreciated.
I see you pass props and use it to your const ServiceItem. You need to use mapStateToProps also then pass to your connect at serviceItem.js.
const mapStateToProps = state => {
return {
selectServices: state.selectServices,
services: state.services
};
};
export default connect(mapStateToProps, actions)(ServiceItem);
Your ServiceDetail.js also should be:
const mapStateToProps = state => {
return {
services: state.services
};
};
export default connect(mapStateToProps, actions)(ServiceDetail);
Your ServiceItem component is just a presentational component, in my opinion, it need not to be connected to redux store, instead, pass the data and event hook from parent to ServiceItem component and that will work just fine. That will solve your props problem as well.
Point made by "DennisFrea" still holds good. i am just suggesting another of achieving the same thing.