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
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} />
));
}
The architecture is like this: I have a plotMap component, which gets a list of plots from state and maps them to a bunch of plotMarker components, which return polygons/markers, based on map zoom (also read from state). If a given plot is selected for editing, the plotMarker component returns a plotPolygon component which is editable. When a user saves the edited plotPolygon component, this updates the corresponding plot in the state plot list.
Problem: the plotMarker's polygon, which is displayed as soon as the edited plotPolygon component is successfully saved, is not updated with the new shape, but keeps the old. Only when one zooms out, and the plotMarker renders its marker component, and zooms back in, and the plotMarker renders its polygon component again, is the new shape displayed.
Could this be due to a lag inside the app? How can I make the plotMarker display the new polygon as soon as it is successfully saved?
plotMap component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Map, TileLayer, LayersControl, MapControl } from 'react-leaflet';
import { GoogleLayer } from './GoogleLayer';
import { geolocated } from 'react-geolocated';
import 'leaflet-geocoder-mapzen';
import SearchBox from './searchBox';
import Control from 'react-leaflet-control';
import { centroid } from '#turf/turf';
import PlotMarker from './plotMarker';
const { BaseLayer } = LayersControl;
const key = 'key';
const hybrid = 'HYBRID';
const terrain = 'TERRAIN';
const road = 'ROADMAP';
const satellite = 'SATELLITE';
const centerLat = props => {
if (
props.isGeolocationAvailable &&
props.isGeolocationEnabled &&
props.coords
) {
return props.coords.latitude;
}
return 32.11;
};
const centerLong = props => {
if (
props.isGeolocationAvailable &&
props.isGeolocationEnabled &&
props.coords
) {
return props.coords.longitude;
}
return 34.963;
};
const mapCenterPoint = props => {
if (props.plots && (props.selectedPlot || props.plotBeingEdited)) {
let ourPlot = props.plots.filter(
plot => plot._id === (props.selectedPlot || props.plotBeingEdited)
)[0];
try {
let center = centroid(ourPlot.feature).geometry.coordinates.reverse();
return { center: center, zoom: 16 };
} catch (e) {
console.log(e);
}
}
return { center: [centerLat(props), centerLong(props)], zoom: 8 };
};
export class PlotMap extends Component {
markers = props => {
if (props.plots) {
return (
<div>
{(props.filteredPlots || props.plots).map(
plot =>
plot &&
plot.feature &&
plot._id && (
<PlotMarker
key={plot._id}
id={plot._id}
name={plot.name}
geoJSON={plot.feature}
/>
)
)}
</div>
);
}
};
render() {
return (
<div
className="col-sm-8 m-auto p-0 flex-column float-right"
style={{ height: `85vh` }}>
<Map
center={mapCenterPoint(this.props).center}
zoom={mapCenterPoint(this.props).zoom}
zoomControl={true}
onZoomend={e => {
this.props.setZoomLevel(e.target.getZoom());
}}
onMoveEnd={e => {
this.props.setMapCenter(e.target.getCenter());
}}>
<LayersControl position="topright">
<BaseLayer name="Google Maps Roads">
<GoogleLayer googlekey={key} maptype={road} />
</BaseLayer>
<BaseLayer name="Google Maps Terrain">
<GoogleLayer googlekey={key} maptype={terrain} />
</BaseLayer>
<BaseLayer checked name="Google Maps Hybrid">
<GoogleLayer
googlekey={key}
maptype={hybrid}
libraries={['geometry', 'places']}
/>
</BaseLayer>
</LayersControl>
<SearchBox postion="bottomright" />
{this.markers(this.props)}
</Map>
</div>
);
}
}
function mapStateToProps(state) {
return {
filteredPlots: state.plots.filteredPlots,
plots: state.plots.plots,
selectedPlot: state.plots.selectedPlot,
mapCenter: state.plots.mapCenter
};
}
export default geolocated({
positionOptions: {
enableHighAccuracy: false
},
userDecisionTimeout: 5000,
suppressLocationOnMount: false
})(connect(mapStateToProps, actions)(PlotMap));
plotMarker component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Marker, Popup, GeoJSON } from 'react-leaflet';
import { centroid } from '#turf/turf';
import PlotPolygon from './plotPolygon';
const position = geoJSON => {
return centroid(geoJSON).geometry.coordinates.reverse();
};
export class PlotMarker extends Component {
render() {
const {
id,
name,
geoJSON,
zoomLevel,
selectedPlot,
plotBeingEdited
} = this.props;
const markerPosition = position(geoJSON);
let style = () => {
return {
color: 'blue'
};
};
if (selectedPlot === id) {
style = () => {
return {
color: 'red'
};
};
}
if (zoomLevel > 14 && plotBeingEdited === id) {
return <PlotPolygon id={id} geoJSON={geoJSON} />;
} else if (zoomLevel > 14) {
return (
<GeoJSON
id={id}
data={geoJSON}
style={style}
onClick={() => {
this.props.selectPlot(id);
}}
/>
);
}
return (
<Marker
id={id}
className="marker"
position={markerPosition}
onClick={() => {
this.props.selectPlot(id);
}}>
<Popup>
<span>{name}</span>
</Popup>
</Marker>
);
}
}
function mapStateToProps(state) {
return {
selectedPlot: state.plots.selectedPlot,
plotBeingEdited: state.plots.plotBeingEdited,
zoomLevel: state.plots.zoomLevel,
plots: state.plots.plots,
filteredPlots: state.plots.filteredPlots
};
}
export default connect(mapStateToProps, actions)(PlotMarker);
plotPolygon component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions';
import { Polygon, FeatureGroup } from 'react-leaflet';
import { EditControl } from 'react-leaflet-draw';
const positions = props => {
return props.geoJSON.geometry.coordinates[0].map(a => [a[1], a[0]]);
};
export class PlotPolygon extends Component {
render() {
const { id, geoJSON } = this.props;
return (
<FeatureGroup>
<EditControl
position="topright"
onEdited={e => {
e.layers.eachLayer(a => {
this.props.updatePlot({
id: id,
feature: a.toGeoJSON()
});
});
}}
edit={{ remove: false }}
draw={{
marker: false,
circle: false,
rectangle: false,
polygon: false,
polyline: false
}}
/>
<Polygon positions={[positions(this.props)]} />;
</FeatureGroup>
);
}
}
function mapStateToProps(state) {
return { plots: state.plots.plots, filteredPlots: state.plots.filteredPlots };
}
export default connect(mapStateToProps, actions)(PlotPolygon);
Original Solution:
Do you need to reset your zoom level after you exit the edit mode? Your logic is very tightly coupled between mapCenterPoint and the if(zoomLevel... portion of plotMarker. I'm wondering if you may have a state that is causing this to always evaluate true after editing (and until manually zooming): props.plots && (props.selectedPlot || props.plotBeingEdited)
Thoughts on Refactoring:
to figure out a more elegant way
Option 1: HOCs
Higher Order Components (or HOCs) might be a great approach for this. You have an example of a HOC already in your code:
export default connect(mapStateToProps, actions)(PlotMarker);
HOCs are really just functions that return something useful to you in the React world. As such, they're a great form of code reuse. connect is a HOC that returns a function in this scenario, but HOCs are often used to return components. So you may have a HOC like plotMarker(zoomLevel, beingEdited) that performs the logic itself to determine which type of marker component to render.
Check out React's Higher Order Component Documentation for more.
Option 2: Single Return Statement
I've always been a fan of using a single return statement for the render method. I haven't dug in super deep to how React handles diffs, but I'm pretty sure from my experience that it responds better this way - so it might remove the need for you to twitch the zoom levels to reset it.
How I would perform all of PlotMarker's logic inside one return:
export class PlotMarker extends Component{
render(){
const {
id,
name,
geoJSON,
zoomLevel,
selectedPlot,
plotBeingEdited
} = this.props;
const markerPosition = position(geoJSON);
let style = () =>{
return {
color: 'blue'
};
};
if(selectedPlot === id){
style = () =>{
return {
color: 'red'
};
};
}
return (
<div>
{
zoomLevel > 14 && plotBeingEdited === id &&
<PlotPolygon id={id} geoJSON={geoJSON}/> ||
zoomLevel > 14 &&
<GeoJSON
id={id}
data={geoJSON}
style={style}
onClick={() =>{
this.props.selectPlot(id);
}}
/> ||
<Marker
id={id}
className="marker"
position={markerPosition}
onClick={() => {
this.props.selectPlot(id);
}}>
<Popup>
<span>{name}</span>
</Popup>
</Marker>
}
</div>
);
}
}
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>
)
})
}