While in parent call data from child - javascript

I have the attached React-Native app which I'm building for a class project. My START/STOP toggle button is in the parent view and my timer function is in my child view. I have the clock ticking down and starting and stopping but I had to update the parent from the child every time we got a time change.
My question is wouldn't it be better if we updated the parent only when the toggle was pressed? Basically, that would mean being able to retrieve data from a child from within the parent. I just can't figure out how to do that or if it's even possible
import React from 'react';
import { Button,StyleSheet, Text, View } from 'react-native';
import ignoreWarnings from 'react-native-ignore-warnings';
ignoreWarnings(['Warning: componentWillMount is deprecated',
'Warning: componentWillReceiveProps is deprecated'])
import PropTypes from 'prop-types'
import { Constants } from 'expo';
import { Card } from 'react-native-elements';
function merge(obj, dict){
console.log('inside merge')
Object.entries(dict).forEach(([k, v]) =>{
if (!obj[k] || typeof v !== 'object') obj[k] = v
else merge(obj[k], v)
})
}
export class Timers extends React.Component{
constructor(props){
super(props)
this.state={name: this.props.name,
min: this.props.min,
sec: this.props.sec,
timer:this.props.timer,
startFlag:this.props.startFlag,
switchFlag:this.props.switchFlag,
currentName:this.props.currentName,}
}
mountTimer(){
// console.log('timer mounted')
// this.interval=setInterval(this.inc,10000)
// console.log(this.interval)
}
clearTimer(){
// console.log('timer cleared')
clearInterval(this.interval)
}
componentWillMount(){
// console.log('will mount timers')
}
componentDidMount(){
// console.log('mount1')
dictionary=this.state
this.props.updateParent(dictionary)
if(this.state.name === 'WORK'){
// console.log('work timer launched')
this.interval=setInterval(this.inc,1000)}
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log('STATIC',nextProps)
return{name: nextProps.name,
min: nextProps.min,
sec: nextProps.sec,
timer:nextProps.timer,
startFlag:nextProps.startFlag,
switchFlag:nextProps.switchFlag,
currentName:nextProps.currentName,}
// Return null to indicate no change to state.
}
shouldComponentUpdate(nextProps,nextState){
console.log('shouldCompnentUpdate')
// if start is false then the timer should be updating
return this.state.timer
}
componentWillUnmount(){
console.log('unmount')
clearInterval(this.interval)
}
inc=()=>{
console.log('TIC',this.state)
if(this.state.startFlag){
if(this.state.sec > 0){
this.setState({sec: String(parseInt(this.state.sec - 1))},function(){
let dictionary={left:{sec:this.state.sec}}
this.props.updateParent(dictionary)
})
}else{
this.setState({sec:'5'})
if (this.state.min > 0){
this.setState({min: String(parseInt(this.state.min - 1))},function(){
let dictionary={left:{min:this.state.min,
sec:5}}
this.props.updateParent(dictionary)
})
}else{
this.setState({min:'9',sec:'0'})
console.log('reset123')
this.clearTimer()
}
}
}
}
render(){
return(
<View style={styles.timer}>
<Text>{this.state.name}</Text>
<Text>{'min: ' + this.state.min + ' sec: ' + this.state.sec }</Text>
<Text>TBD</Text>
</View>
)
}
}
export default class App extends React.Component {
constructor(){
super()
this.state={left:{name:'WORK',
min:2,
sec:0,
timer:false,},
right:{name:'PLAY',
min:2,
sec:0,
timer:false,},
switchSide:false,
currentName:'WORK',
start:false,}
}
updateState(dictionary){
console.log('UPDATE PARENT')
let stateCopy={...this.state}
/* Object.entries(dictionary).map(([key,value])=>{
stateCopy[key] = value.toString()
}) */
console.log('DICTIONARY',dictionary)
merge(stateCopy, dictionary)
this.setState({...stateCopy},function(){
console.log('LEFTLEFTLEFT',this.state,'LEFTSEC')
console.log(this.state.left.min,'LEFTMIN')
})
// console.log(this.state.leftSec,'LEFTSEC')
// })
}
shouldComponentUpdate(nextProps,nextState){
console.log('SHOULD in main app')
return true
}
startToggle(){
if(this.state.currentName === 'WORK'){
stateCopy={...this.state}
stateCopy.left.timer=!this.state.left.timer
stateCopy.start=!this.state.start
console.log('STATECOPY',stateCopy)
this.setState({...stateCopy})
}
console.log('START TOGGLE START TOGGLE',this.state)
}
resetToggle(){
console.log('reset')
}
componentWillMount(){
console.log('will mount main APP')
}
render() {
console.log('RENDER',this.state,'TIMER',this.state.left.timer)
return (
<View style={styles.container}>
<Button title={this.state.start ? 'Stop' : 'Start'} onPress=
{()=>this.startToggle()}/>
<View style={styles.row}>
<Timers
name = {this.state.left.name}
min = {this.state.left.min}
sec = {this.state.left.sec}
timer= {this.state.left.timer}
startFlag = {this.state.start}
switchFlag = {this.state.switchSide}
currentName= {this.state.currentName}
updateParent={(dictionary={})=>{this.updateState(dictionary)}} >
</Timers>
<Timers
name = {this.state.right.name}
min = {this.state.right.min}
sec = {this.state.right.sec}
timer= {this.state.right.timer}
startFlag = {this.state.start}
switchFlag = {this.state.switchSide}
currentName= {this.state.currentName}
updateParent={(dictionary={})=>{this.updateState(dictionary)}} >
</Timers>
</View>
<Button style={styles.top} title='Reset'onPress=
{()=>this.resetToggle()}/>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection:'column',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
timer:{
flex:1,
flexDirection:'column',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
row:{
flex:0,
flexDirection:'row',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
marginTop:55,
marginBottom:55,
},
top:{
},
})

not sure how this works yet, but it appears the answer is to use ref's. I found the attached example by rossipedia
import React, { Component } from 'react';
import { render } from 'react-dom';
class Parent extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
onClick = () => {
this.child.current.getAlert();
};
render() {
return (
<div>
<Child ref={this.child} />
<button onClick={this.onClick}>Click</button>
</div>
);
}
}
class Child extends Component {
getAlert() {
alert('getAlert from Child');
}
render() {
return <h1>Hello</h1>;
}
}
render(<Parent />, document.getElementById('root'));

Related

set.State is undefined

I have a variable in state called isLoading. The idea is to display a loading message while the program is communicating the server, then display the data. However, at ling 24, I get an error:
TypeError: This.setState is not a function (in 'this.setState({ isloadin: false});
import React from "react";
import { StyleSheet, Text, View, AsyncStorage } from "react-native";
var text;
export default class App extends React.Component {
constructor(props) {
super(props);
state = {
isLoading: true
};
}
componentDidMount = () => {
AsyncStorage.getItem("accessToken").then(token => {
postdata(
"http://1.0.0.0:1337/loadTransactions",
{ UserID: 69 },
function(result) {
text = toString(result.Data[1].ID);
text = result.Data[1].Label;
console.log(result.Data[1].Label);
this.setState({
isLoading: false
});
}
);
});
};
render() {
console.log(this.setState.isLoading);
if (this.setState.isLoading) {
console.log(this.setState.isLoading);
return (
<View style={styles.container}>
<Text>Loading....</Text>
</View>
);
} else {
return (
<View style={styles.container}>
<Text>Hi, {text}</Text>
<Text>Test</Text>
</View>
);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center"
}
});
To maintain the context of a function as the same context where the function was lexically defined, you have to use an arrow function:
componentDidMount = () => {
AsyncStorage.getItem("accessToken").then(token => {
postdata(
"http://204.48.23.161:1337/loadTransactions",
{ UserID: 69 },
function(result) {
// ^^^^^^^ use `result => ` here
text = toString(result.Data[1].ID);
text = result.Data[1].Label;
console.log(result.Data[1].Label);
this.setState({
isLoading: false
});
}
);
});
};
this (ref to the instance of class) might not be available inside the context of AsyncStorage. Save this as another variable and use inside:
componentDidMount = () => {
const self = this;
AsyncStorage.getItem("accessToken").then(token => {
postdata(
"http://204.48.23.161:1337/loadTransactions",
{ UserID: 69 },
function(result) {
text = toString(result.Data[1].ID);
text = result.Data[1].Label;
console.log(result.Data[1].Label);
self.setState({
isLoading: false
});
}
);
});
};

Style Imported Custom Component

So I'm importing a custom component TextButton and packaging it inside of another OutlinedButton. I export the class OutlinedButton expecting to see both the props passed and the new styling added to be rendered. However, only the props are being correctly rendered. The extra styling that I added does not appear at all. Any thoughts as to why this occurs?
import React, { Component } from 'react';
import TextButton from './TextButton';
class OutlinedButton extends Component {
render() {
return (
<TextButton {...this.props} style={styles.outlineButtonStyle} />
);
}
}
const styles = {
outlineButtonStyle: {
borderWidth: 1
}
};
export default OutlinedButton;
TextButton class (it's a bit long)
import React, { Component } from 'react';
import { Text, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
class TextButton extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentWillMount() {}
componentWillReceiveProps(newProps) {
if (newProps.theme !== this.props.theme) {
this.determineTheme(newProps.theme);
}
if (newProps.size !== this.props.size) {
this.determineSize(newProps.size);
}
}
// set the theme
determineTheme = function (theme) {
if (theme === 'primary') {
return {
color: '#0098EE'
};
} else if (theme === 'secondary') {
return {
color: '#E70050'
};
} else if (theme === 'default') {
return {
color: '#E0E0E0'
};
}
return {
color: '#E0E0E0'
};
}
// set the size
determineSize = function (size) {
if (size === 'small') {
return {
fontSize: 16
};
} else if (size === 'medium') {
return {
fontSize: 22
};
} else if (size === 'large') {
return {
fontSize: 28
};
}
return {
fontSize: 22
};
}
render() {
const { onPress, children, theme, size } = this.props;
return (
<TouchableOpacity onPress={onPress}>
<Text style={[this.determineTheme(theme), this.determineSize(size)]}>{children}</Text>
</TouchableOpacity>
);
}
}
TextButton.propTypes = {
onPress: PropTypes.func,
title: PropTypes.string,
theme: PropTypes.string,
size: PropTypes.string
};
export default TextButton;
You are not using the style prop passed down to your TextButton component:
render() {
const { onPress, children, theme, size, style } = this.props;
return (
<TouchableOpacity onPress={onPress} style={style}>
<Text style={[this.determineTheme(theme), this.determineSize(size)]}>{children}</Text>
</TouchableOpacity>
);
}
When you set style as below in <TextButton> Component, you are not setting the style on the component, but passing it as props to the component. So you have to access it in <TextButton> as this.props.style and apply it in the child component as Tholl mentioned below. Hope you got it.
render() {
return (
<TextButton {...this.props} style={styles.outlineButtonStyle} />
);
}
}
const styles = {
outlineButtonStyle: {
borderWidth: 1
}
};
SImple example: https://codesandbox.io/s/wn9455x58

Dynamically added view has no style function React Native

I'm using carousel in my app (taken from here https://github.com/nick/react-native-carousel)
I'm trying to add a view each time a user swipe to the right.
That is the code I'm using:
export default class AllRand extends Component
{
constructor(props)
{
super(props);
this.state =
{
myArr: ['Page numebr: ', 'Page numebr: ']
};
}
_onPageSwitchAnimateEnd()
{
this.state.myArr.push('Page numebr: ')
this.setState({
myArr: this.state.myArr
})
}
render()
{
let Arr = this.state.myArr.map((text, index) => {
return <View key={ index } style={ styles.shit1 }><Text>asdasd { index }</Text></View>
})
return (
<Carousel animate={false} hideIndicators={false} onPageChange={() => this._onPressOut()}>
{ Arr }
</Carousel>
);
}
}
I succeed adding a View but it is not with the style, text 'Page number: ' and index...
UPDATE:
changed my code but still doesn't work...
'use strict';
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import Carousel from 'react-native-carousel';
import FactsApiFetcher from './facts-api-handler/facts-api-fetcher'
export default class AllRand extends Component
{
constructor(props)
{
const InitialnumberOfPages = 2;
super(props);
this.state =
{
numberOfPages: InitialnumberOfPages,
Pages: this._setInitialPages(InitialnumberOfPages)
};
}
_onPageSwitchAnimateEnd()
{
let updatedNumberOfPages = this.state.numberOfPages + 1;
let newArr = this._addPage(updatedNumberOfPages);
this.setState({
numberOfPages: updatedNumberOfPages,
Pages: newArr
});
}
render()
{
return (
<Carousel animate={false} hideIndicators={false} onPageChange={() => this._onPageSwitchAnimateEnd()}>
{ this.state.Pages }
</Carousel>
);
}
_setInitialPages(numberOfPages)
{
let tempArr = [];
for(let i = 0; i < numberOfPages; i++)
{
tempArr.push(<View key={ i } style={ styles.shit1 }><FactsApiFetcher/></View>);
}
return tempArr;
}
_addPage(updatedNumberOfPages)
{
return this.state.Pages.concat([<View key={ updatedNumberOfPages - 1 } style={ styles.shit1 }><FactsApiFetcher/></View>]);
}
}
const styles = StyleSheet.create({
shit1: {
alignSelf: 'stretch',
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FFFF00'
}
});
AppRegistry.registerComponent('AllRand', () => AllRand);
So eventually the problem was because there is a bug in the Carousel library I used ( https://github.com/nick/react-native-carousel). From some reason, the callback function that adds the pages is messing up the pages array (don't know why...).]
When I tried using another Carousel library the function worked.
My only advice I can give following this experience is that when you think you tried all the possibilities in order the fix your bug, try changing the libraries you are using (to another same functionality library).

How to render multi components and set individual status in react-native?

I have a component will use map to render multi checkbox, and each checkbox has a callback function "onPress" get by props, the "onPress" function will setState checked, but now when I click on one checkbox, all checkboxs will be chosed, it cause they all use the same state, the goal I wanna choose each checkbox what I just ckick on, I know I can write many state different "onPress" function for each checkbox, but it looks stupid, I will add more checkbox in the future, What's the best and flexiable way to solve the task?
import React, { Component } from 'react'
import { View } from 'react-native'
import { CheckBox } from 'react-native-elements'
const styles = {
CheckBox: {
borderBottomWidth: 0.3,
borderBottomColor: 'gray'
},
checkBox : {
backgroundColor: "#ffffff",
borderWidth: 0
},
text: {
flex: 0.95,
backgroundColor: "#ffffff"
}
}
const languages = ["中文","英文","日文","韓文"]
class Language extends Component {
constructor(props) {
super(props);
this.state = { checked: false };
}
onPress = () => {
this.setState({ checked: !this.state.checked })
}
renderlanguages = () => {
return languages.map((langauge) => {
return(
<View key = { langauge } style = { styles.CheckBox }>
<CheckBox
title = { langauge }
iconRight
containerStyle = { styles.checkBox }
textStyle = { styles.text }
checkedColor = 'red'
checked = { this.state.checked }
onPress = { this.onPress }
/>
</View>
)
})
}
render(){
return(
<View>
{ this.renderlanguages() }
</View>
)
}
}
export default Language;
The behavior is choose all checkbox even though I only choose one now.
You can just pass the langauge (note this is probably a typo for language) variable to the function and us it to identify which one is being checked
onPress = (langauge) => {
this.setState({ [langauge]: { checked: !this.state[langauge].checked } })
}
renderlanguages = () => {
return languages.map((langauge) => {
return(
<View key = { langauge } style = { styles.CheckBox }>
<CheckBox
title = { langauge }
iconRight
//component = { () => {return <TouchableOpacity></TouchableOpacity>}}
containerStyle = { styles.checkBox }
textStyle = { styles.text }
checkedColor = 'red'
checked = { this.state[langauge].checked }
onPress = { () => this.onPress(langauge) }
/>
</View>
)
})
}

create timer with react native using es6

I am looking to add a timer to my app which is built using react native.
I have looked at the link to the timer mixin in the documentation however I have built the rest of the app using es6 so this won't be compatible.
I have tried the below.
In my Main class I have a function called getTimerCountDown
getTimerCountDown() {
setTimeout(() => {
this.setTimeRemaining(this.getTimeRem()-1);
}, 1000);
}
getTimeRem() {
return this.state.timeRemaining;
}
I have tried calling this in componentDidUpdate as shown below. This works as I want it to if I don't make any other interactions with the UI.
If I do (eg I have a button I can click on the view.) as `componentDidUpdate gets called again the conunter gets really quick (as it is getting called x number of times)
componentDidUpdate(){
this.getTimerCountDown();
}
I am not sure if I am completly on the wrong track here or a small change to what I have done can get me what I want.
What is the best way to get a countdown timer working in react native using es6?
Timer Class
on main page
<Timer timeRem={this.getTimeRem()} />
returns
render(){
return (
<View style={styles.container}>
<Text> This is the Timer : {this.props.setTimer} - {this.props.timeRem} </Text>
</View>
)
}
I'm not really sure how that would work even without any other UI interactions. componentDidUpdate is called every time the component is re-rendered, something that happens when the internal state or passed down props have changed. Not something you can count on to happen exactly every second.
How about moving the getTimerCountDown to your componentDidMount method (which is only called once), and then using setInterval instead of setTimeout to make sure the counter is decremented continuously?
Kinda late, but you can try out this component I made for dealing with timers and es6 components in react-native:
https://github.com/fractaltech/react-native-timer
Idea is simple, maintaining and clearing timer variables on the components is a pain, so simply, maintain them in a separate module. Example:
// not using ES6 modules as babel has broken interop with commonjs for defaults
const timer = require('react-native-timer');
// timers maintained in the Map timer.timeouts
timer.setTimeout(name, fn, interval);
timer.clearTimeout(name);
// timers maintained in the Map timer.intervals
timer.setInterval(name, fn, interval);
timer.clearInterval(name);
// timers maintained in the Map timer.immediates
timer.setImmediate(name, fn);
timer.clearImmediate(name);
// timers maintained in the Map timer.animationFrames
timer.requestAnimationFrame(name, fn);
timer.cancelAnimationFrame(name);
Try this
Timer.js
import React, { Component } from "react";
import { View,Text,Button,StyleSheet } from "react-native";
const timer = () => {};
class Timer extends Component {
constructor(props) {
super(props);
this.state = {
remainingTime: 10
};
}
countdownTimer(){
this.setState({remainingTime:10 });
clearInterval(timer);
timer = setInterval(() =>{
if(!this.state.remainingTime){
clearInterval(timer);
return false;
}
this.setState(prevState =>{
return {remainingTime: prevState.remainingTime - 1}});
},1000);
}
render() {
return (
<View style={styles.container}>
<Text>Remaining time :{this.state.remainingTime}</Text>
<Button title ="Start timer" onPress={()=>this.countdownTimer()}/>
</View>
);
}
}
const styles = StyleSheet.create({
container:{
flex:1,
justifyContent:'center',
alignItems:'center',
}
});
export default Timer;
App.js
import React, { Component } from "react";
import { View,Text,Button,StyleSheet } from "react-native";
import Timer from './timer';
export default class App extends Component{
render(
return (<Timer />)
);
}
Here is full code how you can create a timer (pomodoro Timer) in react-native;
Timer.js
import React from 'react'
import {Vibration, View, Button, Text, TextInput, StyleSheet} from 'react-native'
let pomInterval;
export default class Timer extends React.Component {
constructor() {
super();
this.state = {
minutes: 5,
seconds: 0,
workmins: 5,
worksecs: 0,
breakMins: 2,
breakSecs: 0,
timerState: 'WORK TIMER',
btnState: 'Start'
}
}
vibrate = () => {
Vibration.vibrate([500, 500, 500])
}
pomTimer = () => {
pomInterval = setInterval(() => {
let newSec = this.state.seconds;
newSec--;
if(newSec < 0) {
newSec = 59;
this.state.minutes--;
}
this.setState({
seconds: newSec,
})
if(newSec <= 0 && this.state.minutes <= 0) {
this.vibrate();
if(this.state.timerState == 'WORK TIMER') {
this.setState({
timerState: 'BREAK TIMER',
minutes: this.state.breakMins,
seconds: this.state.breakSecs
})
}else {
this.setState({
timerState: 'WORK TIMER',
minutes: this.state.workmins,
seconds: this.state.worksecs
})
}
}
}, 1000);
}
changeWorkMin = mins => {
clearInterval(pomInterval);
this.setState({
minutes: mins || 0,
workmins: mins || 0,
btnState: 'Start'
})
}
changeWorkSec = secs => {
clearInterval(pomInterval);
this.setState({
seconds: secs || 0,
worksecs: secs || 0,
btnState: 'Start'
})
}
changeBreakMin = mins => {
clearInterval(pomInterval);
this.setState({
breakMins: mins || 0,
btnState: 'Start'
})
}
changeBreakSec = secs => {
clearInterval(pomInterval);
this.setState({
breakSecs: secs || 0,
btnState: 'Start'
})
}
// Creating the functionality for the pause/start button
chnageBtnState = () => {
if(this.state.btnState == 'Start') {
this.pomTimer();
this.setState({
btnState: 'Pause'
})
}else {
clearInterval(pomInterval);
this.setState({
btnState: 'Start'
})
}
}
// Creating the functionality for the reset button
reset = () => {
clearInterval(pomInterval);
if(this.state.timerState == 'WORK TIMER') {
this.setState({
minutes: this.state.workmins,
seconds: this.state.worksecs,
btnState: 'Start'
})
}else {
this.setState({
minutes: this.state.breakMins,
seconds: this.state.breakSecs,
btnState: 'Start'
})
}
}
render() {
return (
<View style={styles.viewStyles}>
<Text style={styles.textStyles}>{this.state.timerState}</Text>
<Text style={styles.textStyles}>{this.state.minutes}:{this.state.seconds}</Text>
<Text>
<Button title={this.state.btnState} onPress={this.chnageBtnState} />
<Button title='Reset' onPress={this.reset} />
</Text>
<Text>Work Time:</Text>
<TextInput style={styles.inputStyles} value={this.state.workmins.toString()} placeholder='Work Minutes' onChangeText={this.changeWorkMin} keyboardType='numeric' />
<TextInput style={styles.inputStyles} value={this.state.worksecs.toString()} placeholder='Work Seconds' onChangeText={this.changeWorkSec} keyboardType='numeric' />
<Text>Break Time:</Text>
<TextInput style={styles.inputStyles} value={this.state.breakMins.toString()} placeholder='Break Minutes' onChangeText={this.changeBreakMin} keyboardType='numeric' />
<TextInput style={styles.inputStyles} value={this.state.breakSecs.toString()} placeholder='Break Seconds' onChangeText={this.changeBreakSec} keyboardType='numeric' />
</View>
)
}
}
// Creating a style sheet to write some styles
const styles = StyleSheet.create({
viewStyles: {
alignItems: 'center'
},
textStyles: {
fontSize: 48
},
inputStyles: {
paddingHorizontal: 50,
borderColor: 'black',
borderWidth: 1
}
})
App.js
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Timer from './timer';
export default function App() {
return (
<View style={styles.container}>
<Timer />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
This is how we can create a pomodoro timer, a timer which has both WORK TIMER and BREAK TIMER and it vibrates the phone as one of the timer reaches its end. I also added the input functionality i.e, you can dynamically change the value of the minutes and seconds (whether work timer or break timer is going on). I also added a start/pause button and a reset button.

Categories