ReactJS - How can I check the status of a checkbox from another react component?

0

Here is my work in progress app. My aim is to show surfing spots on a map with its current wind speeds and its difficulty level (based on how high the wind speed is)

https:// lewisd1996. github.io/surferspotter/

I am new to React and I’m still learning as I go.

How can I change the markers on the map with my check-boxes on the left.

Here is my SurfMap class:

import React, { Component } from 'react';
import L from 'leaflet';
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import main from '../styles/main.scss';
import SpotList from "./SpotList.js";
import Spot from "./Spot.js";
import axios from 'axios';
import jsonLocations from '../jsonLocations.json';

export default class SurfMap extends Component {

    constructor() {
        super()
        this.state = {
            spots: [], //THE ARRAY THAT WILL HOLD THE LIST OF SURFING SPOTS
        }
        }

    getSpots = () => { //THE FUNCTION TO POPULATE THE LIST OF SPOTS USING AXIOS

       axios.get("https://api.jsonbin.io/b/5e8733f193960d63f0782ad5/2")
        .then(res => {
            this.setState({
                spots: res.data
            });
        });

    }

    componentDidMount(){
        this.getSpots();
    }

    render() {
        var startPosition = [36.778259, -119.417931] //STARTING POSITION OF THE MAP
        return (
            <>
            {this.state.spots.length ? 
                <Map className="map" center={startPosition} zoom={5}>
                    <TileLayer
                        attribution='&amp;copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                        />
                    {this.state.spots.map (spot => //LOOP THROUGH THE LIST OF SPOTS AND CREATE A SPOT FOR EACH ONE TO BE PLOTTED ONTO THE MAP
                        <Spot {...spot} />
                    )}
                </Map>:
        <p>loading data....</p>}
      </>

        )
    }
}

Here is my Spot class (the spots to go surf that are plotted on the map):

import React, { Component, setState } from 'react'
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import L from 'leaflet';
import axios from 'axios';
import main from '../styles/main.scss'

var owmApiKey = 'HIDING THIS FROM STACKOVERFLOW';

var beginnerIcon = L.icon({ //SETS UP THE PIN ICON THAT IS USED TO PLOT MARKERS ON THE MAP
    iconUrl: process.env.PUBLIC_URL + '/markers/Green-marker.png',
    iconSize: [41,41],
    iconAnchor: [12.5,41],
    popupAnchor: [0, -41]
});

var intIcon = L.icon({ //SETS UP THE PIN ICON THAT IS USED TO PLOT MARKERS ON THE MAP
    iconUrl:  process.env.PUBLIC_URL + '/markers/Red-marker.png',
    iconSize: [41,41],
    iconAnchor: [12.5,41],
    popupAnchor: [0, -41]
});

var expertIcon = L.icon({ //SETS UP THE PIN ICON THAT IS USED TO PLOT MARKERS ON THE MAP
    iconUrl: process.env.PUBLIC_URL + '/markers/Purple-marker.png',
    iconSize: [41,41],
    iconAnchor: [12.5,41],
    popupAnchor: [0, -41]
});


export default class Spot extends Component {

    constructor(props) {
        super()
        this.state = {
            county_name: props.county_name,
            latitude: props.latitude,
            longitude: props.longitude,
            spot_id: props.spot_id,
            spot_name: props.spot_name,
            wind_speed: 0,
        }
    }

    getWindSpeed = (latitude, longitude) => {//THE FUNCTION TO POPULATE THE LIST OF SPOTS USING AXIOS
            axios.get(`https://api.openweathermap.org/data/2.5/weather?lat=${latitude}&lon=${latitude}&appid=${owmApiKey}`)
            .then(res => {
                this.setState({
                    wind_speed: (res.data.wind.speed * 1.944)
                });
            });
    }

    componentDidMount() {
        this.getWindSpeed(this.state.latitude,this.state.longitude);
    }


    render() {
        return(
        <>
            {(() => {
            if (this.state.wind_speed < 8) {
                return (
                    <Marker position={[this.state.latitude,this.state.longitude]} icon={beginnerIcon} className="beginner-marker">
                    <Popup >
                        <p className="marker-label">{this.state.spot_name + ", " + this.state.county_name}<br/>Wind Speed: {this.state.wind_speed} knots<br/>Difficulty: Beginner</p>
                    </Popup>
                    </Marker>    
                )
            } else if (this.state.wind_speed > 8 && this.state.wind_speed < 16) {
                return (
                    <Marker position={[this.state.latitude,this.state.longitude]} icon={intIcon}  className="intermediate-marker">
                    <Popup >
                        <p className="marker-label">{this.state.spot_name + ", " + this.state.county_name}<br/>Wind Speed: {this.state.wind_speed} knots<br/>Difficulty: Intermediate</p>
                    </Popup>
                    </Marker>    
                )
            } else {
                return (
                    <Marker position={[this.state.latitude,this.state.longitude]} icon={expertIcon}  className="expert-marker">
                    <Popup >
                        <p className="marker-label">{this.state.spot_name + ", " + this.state.county_name}<br/>Wind Speed: {this.state.wind_speed} knots<br/>Difficulty: Expert</p>
                    </Popup>
                    </Marker>   
                )
            }
            })()}
            </>
        )
    }

}

My check-boxes are held in my control panel class:

import React, { Component } from 'react';
import main from '../styles/main.scss';

export default class ControlPanel extends Component {
    render() {
        return (
            <div className="control-panel">
                <div className="form-check form-check-inline">
                    <input className="form-check-input" type="checkbox" id="inlineCheckbox1" value="option1"></input>
                    <label className="form-check-label" htmlFor="inlineCheckbox1">Novice</label>
                </div>

                <div className="form-check form-check-inline">
                <input className="form-check-input" type="checkbox" id="inlineCheckbox2" value="option2"></input>
                <label className="form-check-label" htmlFor="inlineCheckbox2">Intermediate</label>
                </div>

                <div className="form-check form-check-inline">
                <input className="form-check-input" type="checkbox" id="inlineCheckbox3" value="option3"></input>
                <label className="form-check-label" htmlFor="inlineCheckbox3">Expert</label>
                </div>
            </div>
        )
    }
}

And my Map and Control Panel are both put into my dashboard and that’s then rendered in the App.js:

import React, { Component } from 'react'
import 'bootstrap/dist/css/bootstrap.min.css';
import main from '../styles/main.scss'
import SurfMap from './SurfMap.js'
import ControlPanel from './ControlPanel.js';
import Spot from './Spot.js';
import SpotList from './SpotList.js';

export default class Dashboard extends Component {
    render() {
        return (
            <div className="dashboard">
                <div className="dashboard__control-panel">
                    <ControlPanel />
                </div>
                    <SurfMap />
            </div>
        )
    }
}

Can you give us a link to the GitHub repo with the code?

Without really having looked at it I’d say you likely want to lift the state up to some parent component of both dashboard and map so you can filter on it (I’m guessing you want to add/remove map markers depending on what is checked). If the dashboard is going to be used for controlling anything else you may want to plan for this now before you need the state of the dashboard inside another component.

You can also look into context or other state management but that seems a bit unnecessary.

Of course! here is the code :

Ok good idea that sounds like what I want to do, however I’m so confused as to how to do it. Coming from a C# background these components in React are really mashing my brain up.

I was going to say, I only see the gh-page compiled code. The master branch just has the beginner CRA code.

As for learning react, read the docs, look at tutorials, read articles, the usual stuff. It can be hard to wrap your head around at first.

Edit: Not really sure how much help this example is. Also, I haven’t written a class component in forever.

My bad my bad, so sorry

There is the public and src folder which should be ok, node_modules isn’t needed im guessing?

Very sorry Randell, my bad. Newbie and all that!

Can you push the package.json file as well please.

Done.

Thanks a lot for the help man I’m getting a headache haha.

It’s much easier to setup redux if you want to communicate state between components…so you’ll need to setup the store, dispatch an action when checking the box and use a reducer to change state. This should automatically refresh all components that use that state

I just checked that context method mentioned above, I haven’t heard of it before, but it might do the job. All I know is react-redux is best practice when communicating state between components. Also when React refreshes the components with new state it only refreshes the exact part where the dynamic value is being used, not the entire component, that’s what’s so great about using React. I hope all this helps, all the best.

@random121 It’s an option. I would hardly call it easy if you need to learn Redux first. It comes with its own problems and it adds complexity. I’m not saying don’t use Redux, but consider if you really need it first.

@squish

The way you are conditionally rendering in the Spots component will only give you some of the markers. Do you not want all the markers by default and just change the class name and icon depending on the wind speed? You can simplify that component and make it more generic. Use one return and change the classes and icons conditionally.

You can add state to the Dashboard component and pass that down to the ControlPanel and SurfMap components. You would pass a handler method to the ControlPanel and pass whatever state it sets to the SurfMap component. The problem is how to do the checkbox state. If it was a dropdown and you only needed to render markers that fit in one condition it would be easier for filtering the state before mapping the spots array.

Which brings us back to the Spot component. You may want to lift its state up one level and pass the data down as props instead. You are already getting most of the data as props, but you are setting the component state based on the props instead of just using the props directly, which you really shouldn’t do. Just use the props directly instead. The only state that needs lifting is wind speed so you can filter the SurfMap state before mapping it. However, that can get a bit complicated as you need to pass the lat/long to the API call for each spot. BTW, you have latitude twice in the “getWindSpeed” method.

If you can live with a dropdown (select element) then I would suggest doing that and only showing one type of marker (difficulty level) or all, and not be able to mix and match difficulty levels using checkboxes.