I can't get any nodes from GoogleMap component - reactjs

I wanna test my map component. I'm using react-testing-library with Jest. I don't know how to get any nodes from <GoogleMap></GoogleMap> (This component is too deep). I've tried get marker and remove button, but It doesn't work. I added comment near remove button (easier to search). Can someone give me any tips?
export const MapWithAMakredInfoWindow = compose(
withProps({
googleMapURL: `https://maps.googleapis.com/maps/api/js?key=${apiKey}&v=3.exp&libraries=geometry,drawing,places`,
containerElement: ( ... ),
withStateHandlers( ... ),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const refs = {};
this.setState(...)
)(props => {
return (
<GoogleMap
...props
>
{props.indicators
.filter( ... )
.map((indicator, index) => {
return (
<Marker
data-testid="marker" // I cannot get it
...props
>
{props.isOpen &&
props.id === indicator.id && (
<InfoBox
...props
>
<InfoBoxContainer>
<InfoContent>{indicator.name}</InfoContent>
<InfoContent>{indicator.street}</InfoContent>
<InfoContent>{indicator.city}</InfoContent>
<InfoContent>{indicator.country}</InfoContent>
<InfoBtn onClick={() => props.remove(indicator.id)} data-testid="removeBtn"> //it doesn't work
Remove Marker
</InfoBtn>
</InfoBoxContainer>
</InfoBox>
)}
</Marker>
);
})}
export class Map extends Component {
constructor(props) {...}
componentDidMount() {...}
addIndicator = (event, geocode) => {...};
remove = id => {...};
render() {
return (
<Wrapper>
<MapWithAMakredInfoWindow
...
/>
</Wrapper>
);
}
}

Related

How to refer to previous state in React's child component?

Let's say I have this component:
class MyForm {
render() {
return (
<>
<h3>Remember my Setting</h3>
<Toggle
value={this.state.rememberMe}
onClick={() => {
this.setState(prevState => ({ rememberMe: !prevState.rememberMe }));
}}
/>
</>
)
}
}
How can I pass this prevState if its wrapped in child component? Is this acceptable?
class MyForm {
render() {
return (
<PartialForm
onClick={(v) => {
this.setState({ rememberMe: v });
}}
/>
)
}
}
const PartialForm = (props) => {
return (
<>
<h3>Remember my Setting</h3>
<Toggle
value={props.rememberMe}
onClick={() => {
props.onClick(!props.rememberMe);
}}
/>
</>
);
}
I want to know if accessing props.rememberMe is the same and safe as accessing prevState.rememberMe in parent component. Thanks
In my opinion, the way you're doing is safe and it has no conflicts at all.
I can simply demonstrate those re-rendering steps here:
Pass the current state to PartialForm
Trigger toggle click props.onClick for updating rememberMe in the upper component
The upper component gets re-rendered, and then your child component will have the latest state of rememberMe
By the way, you also forget to pass rememberMe in <PartialForm />
class MyForm {
render() {
return (
<PartialForm
rememberMe={rememberMe}
onClick={(v) => {
this.setState({ rememberMe: v });
}}
/>
)
}
}
const PartialForm = (props) => {
return (
<>
<h3>Remember my Setting</h3>
<Toggle
value={props.rememberMe}
onClick={() => {
props.onClick(!props.rememberMe);
}}
/>
</>
);
}

Unable to update props in child component

This is my parent Component having state ( value and item ). I am trying to pass value state as a props to child component. The code executed in render method is Performing toggle when i click on button. But when i call the list function inside componentDidMount, Toggle is not working but click event is performed.
import React, { Component } from 'react'
import Card from './Components/Card/Card'
export class App extends Component {
state = {
values : new Array(4).fill(false),
item : [],
}
toggleHandler = (index) => {
console.log("CLICKED");
let stateObject = this.state.values;
stateObject.splice(index,1,!this.state.values[index]);
this.setState({ values: stateObject });
}
list = () => {
const listItem = this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})
this.setState({ item : listItem });
}
componentDidMount(){
// if this is not executed as the JSX is render method is executed everything is working fine. as props are getting update in child component.
this.list();
}
render() {
return (
<div>
{/* {this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})
} */}
{this.state.item}
</div>
)
}
}
export default App
This is my child Component where the state is passed as props
import React from 'react'
const Card = (props) => {
return (
<div>
<section>
<h1>Name : John Doe</h1>
<h3>Age : 20 </h3>
</section>
{props.show ?
<section>Skills : good at nothing</section> : null
}
<button onClick={props.toggleHandler} >Toggle</button>
</div>
)
}
export default Card
I know the componentDidMount is executed only once. but how to make it work except writing the JSX directly inside render method
make a copy of the state instead of mutating it directly. By using [...this.state.values] or this.state.values.slice()
toggleHandler = (index) => {
console.log("CLICKED");
let stateObject = [...this.state.values]
stateObject = stateObject.filter((_, i) => i !== index);
this.setState({ values: stateObject });
}
Also in your render method, this.state.item is an array so you need to loop it
{this.state.item.map(Element => <Element />}
Also directly in your Render method you can just do
{this.state.values.map((data, index) => {
return <Card key = {index}
show = {this.state.values[index]}
toggleHandler = {() => this.toggleHandler(index)} />
})}
In your card component try using
<button onClick={() => props.toggleHandler()}} >Toggle</button>
Value should be mapped inside render() of the class component in order to work
like this:
render() {
const { values } = this.state;
return (
<div>
{values.map((data, index) => {
return (
<Card
key={index}
show={values[index]}
toggleHandler={() => this.toggleHandler(index)}
/>
);
})}
</div>
);
}
check sandbox for demo
https://codesandbox.io/s/stupefied-spence-67p4f?file=/src/App.js

Use react-google-maps methods with recompose

I'm using react google maps for fine tuning the user location after a geo location function, this is part of my Map.js code:
const MyMapComponent = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=AIzaSyAKCqAqiyop85LNl9qUb6OAT1lJupLEnzo&v=3.exp&libraries=geometry,drawing,places",
loadingElement: <div style={{height: `100%`}}/>,
containerElement: <div style={{height: `400px`}}/>,
mapElement: <div style={{height: `100%`}}/>,
}),
withState(),
withHandlers(() => {
const refs = {
map: undefined,
}
return {
onMapMounted: () => ref => {
refs.map = ref
},
onBoundsChanged: ({onBoundsChanged}) => () => {
onBoundsChanged(refs.map.getCenter().lat(), refs.map.getCenter().lng())
}
}
}),
withScriptjs,
withGoogleMap
)((props) =>
<GoogleMap
defaultZoom={8}
defaultCenter={{lat: props.lat, lng: props.lng}}
ref={props.onMapMounted}
onBoundsChanged={props.onBoundsChanged}
>
<Marker position={{lat: props.lat, lng: props.lng}}/>
</GoogleMap>
)
I just want to be able to call the panTo() method in other component via ref, internally i have no problem cause i think the ref passes fine in the mapMounted method ( refs.map.getCenter().lat() ), but how to call thee methods externally, my prob is that im using recompose library. thx in advance.
part of the code in Home.js where i use map and have the get position method that i want to trigger panTo():
import MyMapComponent from './Maps';
export default class HomeComponent extends React.Component {
render() {
const {showAlert, address, lat, lng} = this.props;
return (
<MyMapComponent
lat={lat}
lng={lng}
onBoundsChanged={this.props.handleOnBoundsChanged}
/>
<Button onClick={()=>this.props.getPosition()}>Usar mi
ubicaciĆ³n actual</Button>
)
}
}
You can create a prop out of any of the map methods.
...
withHandlers(() => {
let map;
return {
onMapMounted: () => ref => {
map = ref;
},
onBoundsChanged: ({ onBoundsChanged }) => () => {
onBoundsChanged(map.getCenter().lat(), map.getCenter().lng());
}
mapPanTo: () => args => map.panTo(args),
};
})
...
Now props.mapPanTo can be passed around to any other component.

How to pass props to components described in const in ReactJS

I'm using the react-sortable-hoc library to sort my list of items. More than just listing I need to run a functionality when clicking the single item.
Listing and sorting and everything is working fine. How can I pass a props that should be called clickFunction() which consoles the name when I click the name listed through SortableItem?
import {SortableContainer, SortableElement, arrayMove} from 'react-sortable-hoc';
const SortableItem = SortableElement(({value}) => <li>{value.first_name}</li>);
const SortableList = SortableContainer(({items}) => {
return (
<ul>
{items.map((value, index) => (
<SortableItem key={`item-${index}`} index={index} value={value} />
))}
</ul>
);
});
class Details extends React.Component {
clickFunction(name) {
console.log(name)
}
onSortEnd({oldIndex, newIndex}) {
this.setState({
testlist: arrayMove(this.state.testlist, oldIndex, newIndex),
});
};
render() {
return (
<div>
<SortableList items={this.state.testlist} onSortEnd={this.onSortEnd.bind(this)} pressDelay="200" />
</div>
)
}
}
You can pass the function from Details component and receive it in props of the SortableList and SortableItem like
import {SortableContainer, SortableElement, arrayMove} from 'react-sortable-hoc';
const SortableItem = SortableElement(({value, clickFunction}) => <li onClick={() => clickFunction(value)}>{value.first_name}</li>);
const SortableList = SortableContainer(({items, clickFunction}) => {
return (
<ul>
{items.map((value, index) => (
<SortableItem key={`item-${index}`} index={index} clickFunction={clickFunction} value={value} />
))}
</ul>
);
});
class Details extends React.Component {
clickFunction(name) {
console.log(name)
}
onSortEnd({oldIndex, newIndex}) {
this.setState({
testlist: arrayMove(this.state.testlist, oldIndex, newIndex),
});
};
render() {
return (
<div>
<SortableList items={this.state.testlist} onSortEnd={this.onSortEnd.bind(this)} pressDelay="200" clickFunction={this.clickFunction} />
</div>
)
}
}
This is an example of passing props to SortableContainer:
const SortableList = SortableContainer(({items, props, handleClickFunction, fieldSelector}) => {
const subComponentList = [];
items.map((value, index) =>
subComponentList.push(
<SortableItem
key={`item-${index}`}
index={index}
value={value}
props={props}
handleClickFunction={handleClickFunction}
fieldSelector={fieldSelector}
/>)
);
return (
<ul>{subComponentList}</ul>
);
});

Can React selectors return jsx?

When I write my React applications I keep all state, props calculations in separate functions. I call them selectors, though I'm not sure if those are selectors. By definition, selectors are functions which return subsets of state or props. I have several questions concerning this issue:
1) Can selectors return jsx objects?
For example, I would like to populate <Item /> into another component
selectorItem = (state, props) => {
const { items } = props;
const ItemsJSX = items.map((item) => {
return (
<Item
item={item}
key={item.id}
/>
)
});
return {
items: ItemsJSX
}
}
class Page extends Component {
render() {
return (
<List
{...selectorItem(this.state, this.props)}
/>
)
}
}
Is this a valid selector?
If not, how can I populate JSX into another component using selectors?
2) Should I write selector per component (selector returns object with multiple props) or per prop (separate selector per prop)?
selectorItemsComments = (state, props) => {
const { items } = props;
const { comments } = props;
const ItemsJSX = items.map((item) => {
return (
<Item
item={item}
key={item.id}
/>
)
});
const CommentsJSX = comments.map((comment) => {
<Comment
comment={comment}
key={comment.id}
/>
});
return {
items: ItemsJSX,
comment: CommentsJSX
}
}
versus
selectorItems = (state, props) => {
const { items } = props;
const ItemsJSX = items.map((item) => {
return (
<Item
item={item}
key={item.id}
/>
)
});
return {
items: ItemsJSX
}
}
selectorComments = (state, props) => {
const { comments } = props;
const CommentsJSX = comments.map((comment) => {
return (
<Comment
comment={comment}
key={comment.id}
/>
)
});
return {
comment: CommentsJSX
}
}
Thank you
These are not selectors the are just functions returning components
TL;DR
The short answer is yes you can return Components from functions.
Full Answer
In your simpler first example you should just return the component array from your selectorItem (I have renamed this to renderItems) function:
const renderItems = (items) => items.map((item) =>
<Item
item={item}
key={item.id}
/>
);
const renderComments = (comments) => comments.map((comment) =>
<Comment
item={comment}
key={comment.id}
/>
);
class Page extends Component {
render() {
const {items, comments} = this.props;
return (
<List>
{renderItems(items)}
{renderComments(comments)}
</List>
);
}
}
I would recommend converting renderItems to a Component and then you could do something like:
<List>
<Items items={items} />
<Comments comments={comments} />
</List>
If I understand correctly, your selector is still a React component. This pattern is called "Smart vs Dumb" component, or Presentational and Container Components.
Is HOCs (Higher Order Component) what you want ?

Resources