src/App.js
import React, { Component } from 'react';import mapboxgl from 'mapbox-gl';import 'mapbox-gl/dist/mapbox-gl.css'; // Third-party React componentsimport 'react-select/dist/react-select.css';import 'react-virtualized/styles.css';import 'react-virtualized-select/styles.css';import Select from 'react-virtualized-select';import createFilterOptions from 'react-select-fast-filter-options'; // Custom React componentsimport ControlPanel from './components/control-panel';import Section from './components/section';import InputGroup from './components/input-group';import Legend from './components/legend';import ConnectivityChart from './components/connectivity-chart'; // Helpersimport {calculate_index} from './helpers/helper-index-scores';import apiConfig from './helpers/api-config';import countConnectivity from './helpers/count-connectivity'; // Main styleimport './App.css';// Map colorsconst mapColors = { // higher color will be shown where indexes are 1 higher: '#0068EA', // lower color will be shown where indexes are 0 lower: '#DCDCDC'} mapboxgl.accessToken = apiConfig.accessTokenclass App extends Component { constructor(props: Props) { super(props); this.state = { map: {}, lng: -74.2973, lat: 4.5709, zoom: 4.5, connectivityTotals: null, options: [], searchValue: '', schools: {}, regions: {} }; } Function `componentDidMount` has 118 lines of code (exceeds 25 allowed). Consider refactoring. componentDidMount() { const map = new mapboxgl.Map({ container: this.mapContainer, style: 'mapbox://styles/mapbox/streets-v9', center: [this.state.lng, this.state.lat], zoom: this.state.zoom }); this.setState({map}); // Promises let shapesPromise = fetch(apiConfig.shapes).then((response) => response.json()) let schoolsPromise = fetch(apiConfig.schools).then((response) => response.json()) let mapLoadPromise = new Promise((resolve, reject) => { map.on('load', (e) => { resolve(map) }) map.on('error', (e) => { reject(map) }) }) // Set data for regions when regions and map are availableSimilar blocks of code found in 2 locations. Consider refactoring. Promise.all([shapesPromise, mapLoadPromise]).then(([geojson, map]) => { map.getSource('regions').setData(geojson) }) // Set data for schools when regions and map are availableSimilar blocks of code found in 2 locations. Consider refactoring. Promise.all([schoolsPromise, mapLoadPromise]).then(([geojson, map]) => { map.getSource('schools').setData(geojson) }) // Handle shapes data shapesPromise.then(function(myJson) { // Calculate indexes myJson.features = calculate_index( myJson.features, 'population', 'pop' ) myJson.features = calculate_index( myJson.features, 'threats', 'threats_index' ) myJson.features = calculate_index( myJson.features, 'violence', 'violence_index' ) return myJson }) // Handle school data schoolsPromise.then((geojson) => { this.setState({ connectivityTotals: countConnectivity(geojson.features) }) }) // When data arrives, process them in the background // to build a list of names for the search component Promise.all([schoolsPromise, shapesPromise]).then(([schoolsGeojson, shapesGeojson]) => { return new Promise((resolve, reject) => { let webWorker = new Worker('ww-process-names.js') webWorker.onmessage = (event) => { resolve(event.data) } webWorker.onerror = (err) => { reject(err) } // send geojsons to worker webWorker.postMessage([schoolsGeojson, shapesGeojson]) }) }).then((options) => { this.setState({ options, filter: createFilterOptions({options}) }) }) map.on('move', () => { const { lng, lat } = map.getCenter(); this.setState({ lng: lng.toFixed(4), lat: lat.toFixed(4), zoom: map.getZoom().toFixed(2) }); }); map.on('load', function(e) { map.addLayer({ id: 'regions', type: 'fill', // Add a GeoJSON source containing place coordinates and information. source: { type: 'geojson', data: { type: "FeatureCollection", features: [] } }, layout: { visibility: 'none' }, paint: { 'fill-opacity': 0.5 } }); map.addLayer({ id: 'schools', type: 'circle', // Add a GeoJSON source containing place coordinates and information. source: { type: 'geojson', data: { type: "FeatureCollection", features: [] } }, paint: { 'circle-radius': { 'base': 1.75, 'stops':[[12, 2], [22, 180]] }, 'circle-color': ['get', 'color'] } }); // Add click event to schools layer map.on('click', 'schools', (e) => { let coordinates = e.features[0].geometry.coordinates.slice() let schoolProperties = e.features[0].properties // output all properties besides color let html = Object.keys(schoolProperties) .filter((key) => key !== 'color') .reduce((acc, key) => { return acc + `<p><strong>${key}:</strong> ${schoolProperties[key]}</p>` }, '') new mapboxgl.Popup().setLngLat(coordinates).setHTML(html).addTo(map) }) // Change the cursor to a pointer map.on('mouseenter', 'schools', (e) => { map.getCanvas().style.cursor = 'pointer' }) map.on('mouseleave', 'schools', (e) => { map.getCanvas().style.cursor = '' }) }); } displayLayerHandler(e) { // layer name should be stored in element's value property let layerName = e.target.getAttribute('value') // will be 'visible' or 'none' let currentState = e.target.checked ? 'visible' : 'none' // Set layer visibility this.state.map.setLayoutProperty(layerName, 'visibility', currentState) } changeRegionPaintPropertyHandler(e) { // Get all checked inputs for regions let matches = document.querySelectorAll("input[name=region]:checked"); // Change layer visibility if there are matches this.state.map.setLayoutProperty('regions', 'visibility', matches.length ? 'visible' : 'none') if (!matches.length) { // no region selected return } // build the aggregation query let atts_to_aggregate = Array.prototype.slice.call(matches).reduce((a,t) => { a.push(['get', t.value]) return a }, ['+']) // Set new paint property to color the map this.state.map.setPaintProperty( 'regions', 'fill-color', // linear interpolation for colors going from lowerColor to higherColor accordingly to aggregation value ['interpolate', ['linear'], ['/', atts_to_aggregate, atts_to_aggregate.length-1], 0, mapColors.lower, 1, mapColors.higher ] ) } Function `render` has 58 lines of code (exceeds 25 allowed). Consider refactoring. render() { return ( <div className="App"> <div> <div ref={el => this.mapContainer = el} className="mainMap" /> </div> <ControlPanel> <Select name="search" placeholder="School or municipality" multi={true} className="search" value={this.state.searchValue} onChange={(selectedOption) => { let regionFilter = null let schoolFilter = null // Set current state this.setState({searchValue: selectedOption}) if (selectedOption.length) { // Create filter for regions to look into all 'NOMBRE's regionFilter = ['any'].concat(...selectedOption.map((input) => { return [ ['==', ['get', 'NOMBRE_C'], input.value], ['==', ['get', 'NOMBRE_D'], input.value], ['==', ['get', 'NOMBRE_M'], input.value] ] })) // Create filter for schools to look into every 'name' schoolFilter = ['any'].concat(selectedOption.map((input) => { return ['==', ['get', 'name'], input.value] })) } // Set filters this.state.map.setFilter('regions', regionFilter) this.state.map.setFilter('schools', schoolFilter) }} filterOptions={this.state.filter} options={this.state.options} arrowRenderer={() => <i className="fas fa-search" />} />Similar blocks of code found in 2 locations. Consider refactoring. <Section title="Region threats"> <InputGroup type="checkbox" name="region" group={[ { value: 'threats_index', label: 'Natural Disasters Index' }, { value: 'violence_index', label: 'Violence Index' } ]} onChange={this.changeRegionPaintPropertyHandler.bind(this)} /> </Section>Similar blocks of code found in 2 locations. Consider refactoring. <Section title="Region vulnerabilities"> <InputGroup type="checkbox" name="region" group={[ { value: 'hdi', label: 'Human Development Index' }, { value: 'pop', label: 'Population' } ]} onChange={this.changeRegionPaintPropertyHandler.bind(this)} /> </Section> <Section title="School Capabilities"> <InputGroup type="checkbox" name="school" group={[ { value: 'schools', label: 'Connectivity', onChange: this.displayLayerHandler.bind(this), defaultChecked: 'checked' } ]} onChange={(e) => {}} /> </Section> <p className="controlPanel__footerMessage">The selected items will be considered when calculating the risk level of schools and areas.</p> <Section title="Connectivity Details"> <ConnectivityChart totals={this.state.connectivityTotals}></ConnectivityChart> </Section> </ControlPanel> <Legend from={mapColors.higher} to={mapColors.lower} steps={10} leftText="Most Risk" rightText="Least Risk" /> </div> ); }} export default App;