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).
Related
I'm new to react native and want to make one function change state for the clicked button only not others that have the same function
as I explained in the title here is an example code
please any help & I know it might be a selly question but any answer will help
thanks a lot
export default class App extends Component {
constructor(){
super();
this.state = {
opened: true,
}
}
componentHideAndShow = () =>{
this.setState(previousState => ({opened: !previousState.opened}))
}
render() {
return (
{
this.state.opened ? <Text> hello</Text> : <Text> hello sdfsdfsdf</Text>
}
<Text onPress={this.componentHideAndShow}>test</Text>
{
this.state.opened ? <Text> hello</Text> : <Text> hello sdfsdfsdf</Text>
}
<Text onPress={this.componentHideAndShow}>test</Text>
);
}
}
This should work.
import React, { Component } from 'react';
import { View, Text, Button } from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
opened: [true, true]
};
}
componentHideAndShow = index => {
const opened = this.state.opened;
opened[index] = !opened[index];
this.setState({ opened: opened });
};
render() {
return (
<View>
{this.state.opened[0] ? (
<Text> hello</Text>
) : (
<Text> hello sdfsdfsdf</Text>
)}
<Button onPress={() => this.componentHideAndShow(0)}>test</Button>
{this.state.opened[1] ? (
<Text> hello</Text>
) : (
<Text> hello sdfsdfsdf</Text>
)}
<Button onPress={() => this.componentHideAndShow(1)}>test</Button>
</View>
);
}
}
Edit: you can do like this if you don't know the number of items:
import React, { Component } from 'react';
import { View, Text, Button } from 'react-native';
const myArrayOfStrings = ['hello1', 'hello2', 'hello3', 'hello4', 'hello5'];
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
opened: undefined
};
}
componentDidMount() {
let opened = [];
myArrayOfStrings.map(item => {
opened.push(true);
});
this.setState({ opened: opened });
}
componentHideAndShow = index => {
const opened = this.state.opened;
opened[index] = !opened[index];
this.setState({ opened: opened });
};
render() {
const output = myArrayOfStrings.map((item, index) => {
return (
<View>
<Text>
{this.state.opened[index]
? `${item} is opened`
: `${item} is opened`}
</Text>
<Button onPress={() => this.componentHideAndShow(0)}>test</Button>
</View>
);
});
return <View>{output}</View>;
}
}
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'));
I am near the end of creating my application.
So it is for banks accounts where they ask you to give the first letter of your password, then for example fourth, etc.
I'm tired of counting on my own so I created this app.
But there is the last bug that I don't know how to fix.
So when I press "1" I get "1 - H", and then when I press "4" I want to get:
"1 - H" (clicked before)
"4 - X" (clicked just now)
but instead, I get:
"4 - X" (clicked just now)
"4 - X" (clicked just now)
So it is caused by the way handleResults() function works inside my Input component, but for now it is my only concept how to approach this...
import React, { Component } from 'react';
import TextField from 'material-ui/TextField';
import './style.css';
import Buttons from '../Buttons';
import Results from '../Results';
class Input extends Component {
constructor(props) {
super(props);
this.state = {
password: 'Hh9Xzke2ayzcEUPHuIfS',
selectedButtons: [],
};
this.handleButtonSelectTwo = this.handleButtonSelectTwo.bind(this);
}
handleInputChange(pass) {
this.setState({ password: pass });
}
handleButtonSelectTwo(selected) {
this.setState({
selectedButtons: [...this.state.selectedButtons, selected],
});
}
handleResults() {
return this.state.selectedButtons.map(el => (
<Results key={el} appState={this.state} />
));
}
render() {
return (
<div>
<div className="Input-textfield">
<TextField
hintText="Paste your password here to begin"
value={this.state.password}
onChange={event => this.handleInputChange(event.target.value)}
/>
</div>
<div>
<Buttons
handleButtonSelectOne={this.handleButtonSelectTwo}
array={this.state.password.length}
/>
{this.handleResults()}
</div>
</div>
);
}
}
export default Input;
and here is Results component code:
import React, { Component } from 'react';
import _ from 'lodash';
import Avatar from 'material-ui/Avatar';
import List from 'material-ui/List/List';
import ListItem from 'material-ui/List/ListItem';
import './style.css';
const style = {
avatarList: {
position: 'relative',
left: -40,
},
avatarSecond: {
position: 'relative',
top: -40,
left: 40,
},
};
class Results extends Component {
resultsEngine(arg) {
const { selectedButtons, password } = this.props.appState;
const passwordArray = password.split('').map(el => el);
const lastSelectedButton = _.last(selectedButtons);
const passwordString = passwordArray[_.last(selectedButtons) - 1];
if (arg === 0) {
return lastSelectedButton;
}
if (arg === 1) {
return passwordString;
}
return null;
}
render() {
if (this.props.appState.selectedButtons.length > 0) {
return (
<div className="test">
<List style={style.avatarList}>
<ListItem
disabled
leftAvatar={<Avatar>{this.resultsEngine(0)}</Avatar>}
/>
<ListItem
style={style.avatarSecond}
disabled
leftAvatar={<Avatar>{this.resultsEngine(1)}</Avatar>}
/>
</List>
</div>
);
}
return <div />;
}
}
export default Results;
Anyone has an idea how should I change my code inside handleResults() function to achieve my goal? Any help with solving that problem will be much appreciated.
Buttons component code:
import React from 'react';
import OneButton from '../OneButton';
const Buttons = props => {
const arrayFromInput = props.array;
const buttonsArray = [];
for (let i = 1; i <= arrayFromInput; i++) {
buttonsArray.push(i);
}
const handleButtonSelectZero = props.handleButtonSelectOne;
const allButtons = buttonsArray.map(el => (
<OneButton key={el} el={el} onClick={handleButtonSelectZero} />
));
if (arrayFromInput > 0) {
return <div>{allButtons}</div>;
}
return <div />;
};
export default Buttons;
And OneButton code:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const style = {
button: {
margin: 2,
padding: 0,
minWidth: 1,
},
};
class OneButton extends Component {
constructor() {
super();
this.state = { disabled: false };
}
handleClick() {
this.setState({ disabled: !this.state.disabled });
this.props.onClick(this.props.el);
}
render() {
return (
<RaisedButton
disabled={this.state.disabled}
key={this.props.el}
label={this.props.el}
style={style.button}
onClick={() => this.handleClick()}
/>
);
}
}
export default OneButton;
In your resultsEngine function in the Results component you are specifying that you always want the _.last(selectedButtons) to be used. This is what it is doing, hence you always see the last button clicked. What you actually want is the index of that iteration to show.
const lastSelectedButton = selectedButtons[this.props.index];
const passwordString = passwordArray[selectedButtons[this.props.index]];
To get an index you have to create and pass one in, so create it when you map over the selected Buttons in the handleResults function in your Input component.
handleResults() {
return this.state.selectedButtons.map((el, index) => (
<Results key={el} appState={this.state} index={index} />
));
}
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>
)
})
}
I'm going around in circles trying to figure out why I can't get section headers to stick at the top of my React Native <ListView>, but nothing seems to be helping.
I am pretty sure that I am implementing the required method renderSectionHeader correctly and my dataBlob object is correctly structured - I am able to get the <ListView> to render fine, it's just the section headers aren't sticking...
Can anyone see something obvious that I've overlooked?
Root Component:
import React, { Component } from 'react';
import {
View
} from 'react-native';
import { connect } from 'react-redux';
import {
Container,
Header,
Content
} from 'native-base';
import TabFooter from '../../Components/Footer';
import ListViewWithSections from '../../Components/ListViewWithSections';
class Accommodation extends Component {
componentWillMount() {
// function that structures data into something suitable for ListView
const formatDataForListView = accommodation => {
const dataBlob = {};
const sectionIDs = [];
const rowIDs = [];
let sectionID = -1;
accommodation.forEach((accom) => {
sectionID++;
sectionIDs.push(accom.id);
dataBlob[accom.id] = accom.name;
rowIDs[sectionID] = []; // initialise empty array associated with Section ID
accom.guests.forEach((guest) => {
rowIDs[sectionID].push(guest.id);
dataBlob[accom.id + ':' + guest.id] = guest;
});
});
console.log('dataBlob: ', dataBlob);
console.log('sectionIDs: ', sectionIDs);
console.log('rowIDs: ', rowIDs);
return { dataBlob, sectionIDs, rowIDs };
};
const { accommodation } = this.props;
const { dataBlob, sectionIDs, rowIDs } = formatDataForListView(accommodation);
this.setState({ dataBlob, sectionIDs, rowIDs });
}
render() {
return (
<Container>
<Header />
<Content>
<ListViewWithSections
dataBlob={this.state.dataBlob}
sectionIDs={this.state.sectionIDs}
rowIDs={this.state.rowIDs}
/>
</Content>
<View>
<TabFooter />
</View>
</Container>
);
}
}
const mapStateToProps = state => {
return { accommodation: state.accommodation };
};
export default connect(mapStateToProps)(Accommodation);
ListViewWithSections.js:
import React, { Component } from 'react';
import {
ListView,
Row,
Text,
View
} from 'react-native';
class ListViewWithSections extends Component {
constructor(props) {
super(props);
const getSectionData = (dataBlob, sectionID) => dataBlob[sectionID];
const getRowData = (dataBlob, sectionID, rowID) => dataBlob[sectionID + ':' + rowID];
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2,
getSectionData,
getRowData
});
const { dataBlob, sectionIDs, rowIDs } = this.props;
console.log('RowIDs new: ', rowIDs);
this.state = {
dataSource: ds.cloneWithRowsAndSections(dataBlob, sectionIDs, rowIDs)
};
}
render() {
console.log('ListView this: ', this);
const renderRow = (data) => {
return (
<View>
<Text>{data.name}</Text>
</View>
);
};
const renderSectionHeader = (sectionData, sectionID) => {
console.log('sectionData: ', sectionData);
console.log('sectionID: ', sectionID);
return (
<View>
<Text style={{ fontWeight: '700' }}>{sectionData}</Text>
</View>
);
};
return (
<ListView
dataSource={this.state.dataSource}
renderRow={renderRow}
renderSectionHeader={renderSectionHeader}
/>
);
}
}
export default ListViewWithSections;
I discovered from this comment to an issue posted where somebody wanted to not have sticky headers that if the <ListView> is nested inside a <ScrollView> then the sticky headers don't stick.
I have been using NativeBase as a basis for building my app's UI and the <Content> component, I believe, inherits from <ScrollView>.
Anyway, when I take it out of the <Content> and <Container> components the sticky headers work fine.