React Leaflet: setting a polygon's style dynamically - reactjs

How do I change a polygon's color programatically?
The solution I was using for GeoJSONs here doesn't work. Though when I inspect the element, I can see
style:{color: "red"}
nonetheless, the map shows a blue polygon.
Here's the relevant part of my 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 (
<Polygon
ref="plot"
style={style()}
id={id}
positions={[positions(this.props)]}
onClick={() => {
this.props.selectPlot(id);
}}
/>
);
}

Pass on color prop as an object:
<Polygon
...
color={selectedPlot === id ? 'red' : 'blue'}
/>

Related

Reactjs - How use "return </Paper>" as code in my html?

I want to add into my code some tags <Paper> and </Paper>. But I didn't manage to do it. These two tags farmes my html code. But, when I runed my code, it show my them as string :
const test = () => {
const paperStyleTop = (nameMinistry) => {
if (nameMinistry === "justice"
) {
return `<Paper elevation={3} sx={{ ...(alertError && { border: "2px solid rgba(255,0,0,0.5)" }) }}>`
} else {
return `<Paper elevation={3}>`
}
}
const paperStyleBottom = () => {
return `</Paper>;`
}
const arrayMinistries = [
{
icon: "balance",
nameMinistry: "Justice",
id: "mJustice",
useStateFullName: ministers.justice.fullName
},
{
icon: "local_police",
nameMinistry: "Intérieur",
id: "mInterieur",
useStateFullName: ministers.interieur.fullName
}
]
return (
{arrayMinistries.map((ministry) => (
<Grid item}>
{paperStyleTop(ministry.nameMinistry)}
// Html code...
{paperStyleBottom()}
</Grid>
))}
)
export default test;
Could you explain to me how I can do to add these pieces of lines to my code ?
************** SOLUTION ***************
With that propose below it dit like this and that work :
const test = () => {
const paperProps = (nameMinistry) => {
const props = {
elevation: 3,
};
if (nameMinistry === "mJustice" ||
nameMinistry === "mInterieur" ||
nameMinistry === "mEducationNationale" ||
nameMinistry === "mSante" ||
nameMinistry === "mArmees" ||
nameMinistry === "mEconomieFinance"
) {
props.sx = { ...(alertError && { border: "2px solid rgba(255,0,0,0.5)" }) };
} else {
props.sx = {}
}
return props;
}
const arrayMinistries = [
{
icon: "balance",
nameMinistry: "Justice",
id: "mJustice",
useStateFullName: ministers.justice.fullName
},
{
icon: "local_police",
nameMinistry: "Intérieur",
id: "mInterieur",
useStateFullName: ministers.interieur.fullName
}
]
return (
{arrayMinistries.map((ministry) => (
<Grid item}>
<Paper {...paperProps(ministry.id)}>
// Html code...
</Paper>
</Grid>
))}
)
export default test;
This sounds like an XY problem to me. It appears that you want to pass specific props into your <Paper> component: what don't you object spread the props dictionary into it instead?
You can use useMemo() to memoize the props you want to spread, so that the object will be updated based on changes in the dependency array.
Example:
const test = () => {
const paperProps = useMemo(() => {
const props = {
elevation: 3,
};
if (ministry.nameMinistry === 'justice') {
props.sx = { ...(alertError && { border: "2px solid rgba(255,0,0,0.5)" }) };
}
return props;
}, [ministry.nameMinistry])
return (
<Grid item>
<Paper {...paperProps}>
{/* More content here */}
</Paper>
</Grid>
)
}

Recharts Line chart as imported functional component not rendering

I have the following functional component that takes in some data and render a line chart. However depending on the type of data I would like to change the type of chart being used, i.e. (Line, Bar, etc).
As such I have a main component FieldCompareChart. This component determines what type of chart should be displayed based on the data graphs prop and determines if the yAxis should be on the left or right. It then renders the ResponsiveContainer and calls BuildYaxis as shown below.
I have the yAxis types is separate files as function components that return the type of chart being used.
Having it this way does not render the lines in the graph.
However if I pass the contents of dbVhChart directly into the return statement of the if clause of the BuildYAxis function, this works. I am trying to understand why importing doesn't but the other does.
import React from "react";
import {
LineChart,
XAxis,
CartesianGrid,
Legend,
ResponsiveContainer,
ReferenceArea,
YAxis,
Line,
} from "recharts";
import axisConfigs from "./yAxisConfig.json";
import DbVhChart from "./yAxisTypes/dbVh";
import DbVvChart from "./yAxisTypes/dbVv";
import NdviChart from "./yAxisTypes/ndvi";
function BuildYaxis(props) {
const { data, graphs, activeCrop, hideCrops } = props;
return Object.keys(data[0])
.filter((n) => ["date", "timestamp"].indexOf(n) === -1)
.map((c, index) => {
const sub = c.split("_").pop();
const idx = graphs.findIndex((val) => val === sub);
const chartProps = {
idx,
axisConfig: axisConfigs[sub],
activeCrop,
hideCrops,
dataKey: c,
};
if (sub === "dbVv") {
return <DbVvChart key={`dbVv-${index}`} {...chartProps} />;
}
return null;
});
}
const FieldCompareChart = (props) => {
const {
data,
activeCrop,
setActiveCrop,
hideCrops,
toggleCrop,
syncId,
showLegend,
left,
right,
refAreaLeft,
refAreaRight,
setRefAreaLeft,
setRefAreaRight,
zoom,
zoomOut,
graphs,
top,
bottom,
top2,
bottom2,
} = props;
if (data && data.length) {
return (
<div
className="highlight-bar-charts"
style={{ userSelect: "none", height: "calc(100vh - 100px)" }}
>
<button type="button" className="btn update" onClick={zoomOut}>
Zoom Out
</button>
<ResponsiveContainer width="100%" height="92%">
<LineChart
data={data}
onMouseLeave={() => setActiveCrop(null)}
onMouseDown={(e) => {
// this prevents an error from being thrown when the legend is clicked in the graphs
if (!e) return;
setRefAreaLeft(e);
}}
onMouseMove={(e) => {
if (!e) return;
refAreaLeft && setRefAreaRight(e);
}}
// eslint-disable-next-line react/jsx-no-bind
onMouseUp={(e) => {
// this prevents an error from being thrown when the legend is clicked in the graphs
if (!e) return;
if (!refAreaRight) {
return setRefAreaLeft("");
}
zoom();
}}
syncId={syncId}
width={500}
height={300}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis
domain={[left, right]}
allowDataOverflow
dataKey="date"
type="category"
tickFormatter={(a) => {
if (a === "auto") {
return a;
}
return new Date(a).toLocaleDateString();
}}
/>
{Object.keys(data[0])
.filter((n) => ["date", "timestamp"].indexOf(n) === -1)
.map((c, index) => {
const sub = c.split("_").pop();
const idx = graphs.findIndex((val) => val === sub);
return (
<YAxis
key={`yAxis-key-${index}`}
allowDataOverflow
domain={idx === 0 ? [bottom, top] : [bottom2, top2]}
type="number"
yAxisId={idx}
tickFormatter={(a) => parseFloat(a).toFixed(1)}
orientation={idx === 0 ? "left" : "right"}
/>
);
})}
{showLegend && (
<Legend
onClick={toggleCrop}
onMouseOver={(e) => setActiveCrop(e.dataKey)}
/>
)}
{BuildYaxis({ data, graphs, activeCrop, hideCrops })}
{refAreaLeft && refAreaRight ? (
<ReferenceArea
yAxisId="1"
x1={refAreaLeft}
x2={refAreaRight}
strokeOpacity={0.3}
/>
) : null}
</LineChart>
</ResponsiveContainer>
</div>
);
}
return null;
};
export default FieldCompareChart;
import React from "react";
import { Line } from "recharts";
import randomcolor from "randomcolor";
function DbVhChart(props) {
console.log(props);
const { idx, axisConfig, activeCrop, hideCrops, dataKey } = props;
return (
<Line
key={`comparison-chart-${dataKey}`}
yAxisId={idx}
type="natural"
animationDuration={300}
connectNulls
strokeDasharray={""}
dataKey={dataKey}
strokeWidth={activeCrop === null ? 1 : activeCrop === dataKey ? 3 : 1}
stroke={randomcolor({
luminosity: "bright",
seed: axisConfig.seed,
format: "rgba",
alpha: activeCrop === null || activeCrop === dataKey ? 1 : 0.2,
})}
hide={hideCrops.indexOf(dataKey) >= 0}
/>
);
}
export default DbVhChart;
Not working
...
function BuildYaxis(props) {
const { data, graphs, activeCrop, hideCrops } = props;
return Object.keys(data[0])
.filter((n) => ["date", "timestamp"].indexOf(n) === -1)
.map((c, index) => {
const sub = c.split("_").pop();
const idx = graphs.findIndex((val) => val === sub);
const chartProps = {
idx,
axisConfig: axisConfigs[sub],
activeCrop,
hideCrops,
dataKey: c,
};
console.log(`dbVv-${index}`);
if (sub === "dbVv") {
return <DbVvChart key={`dbVv-${index}`} {...chartProps} />;
}
return null;
});
}
...
Working:
...
function BuildYaxis(props) {
const { data, graphs, activeCrop, hideCrops } = props;
return Object.keys(data[0])
.filter((n) => ["date", "timestamp"].indexOf(n) === -1)
.map((c, index) => {
const sub = c.split("_").pop();
const idx = graphs.findIndex((val) => val === sub);
const chartProps = {
idx,
axisConfig: axisConfigs[sub],
activeCrop,
hideCrops,
dataKey: c,
};
if (sub === "dbVv") {
return (
<Line
key={`comparison-chart-${c}`}
yAxisId={idx}
type="natural"
animationDuration={300}
connectNulls
strokeDasharray={""}
dataKey={c}
strokeWidth={activeCrop === null ? 1 : activeCrop === c ? 3 : 1}
stroke={randomcolor({
luminosity: "bright",
seed: axisConfigs[sub].seed,
format: "rgba",
alpha: activeCrop === null || activeCrop === c ? 1 : 0.2,
})}
hide={hideCrops.indexOf(c) >= 0}
/>
);
}
return null;
});
}
...

How to make custom renderer interactive? (listen to component state update, and rerender)

I added checkbox into my custom renderer, but it doesn't listen to state update. Do you have any ideas how to make state working in custom renderer?
I have red, that in v5.0.1 you can use as a renderer -- react components. But I can't find details in docs.
const quizRenderer = (htmlAttribs, children) => {
const { type, quizid: quizId } = htmlAttribs
return <View key={`quiz-${quizId}`}>{children}</View>
}
const variantRenderer = (
htmlAttribs,
children,
convertedCSSStyles,
passProps
) => {
const { quizid: quizId, variantid: variantId, type } = htmlAttribs
console.log('userAnswers', userAnswers)
const handlePressQuizVariant = (quizId, variantId) => () => {
let quizAnswers = userAnswers[quizId] || []
//if this answer also been set, we remove it
// if there was clear cell -- we add it
if (quizAnswers.includes(variantId)) {
quizAnswers = quizAnswers.filter(elem => elem !== variantId)
} else {
quizAnswers.push(variantId)
}
setUserAnswers(userAnswers => {
const newUserAnswers = [...userAnswers]
newUserAnswers[quizId] = quizAnswers
return newUserAnswers
})
}
return (
<TouchableOpacity
key={`variant-${quizId}-${variantId}`}
onPress={handlePressQuizVariant(quizId, variantId)}
>
<Text>
<CheckBox
size={18}
checked={userAnswers[quizId].includes(variantId)}
{...(type === 'single'
? { checkedIcon: 'dot-circle-o', uncheckedIcon: 'circle-o' }
: {})}
containerStyle={{ margin: 0, padding: 0 }}
/>{' '}
{children}
</Text>
</TouchableOpacity>
)
}

How to render only 5 items in react autosuggest?

I'am using react autosuggest npm package to get the json data and display it. I want to display only 5 items. How to do it?
Form.js
import React from 'react'
import Autosuggest from 'react-autosuggest';
import cities from 'cities.json';
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
);
);
};
const getSuggestionValue = suggestion => suggestion.name;
const renderSuggestion = suggestion => (
<div>
{console.log('suggestion', suggestion)}
{suggestion.name}
</div>
);
class Form extends React.Component {
constructor() {
super();
this.state = {
value: '',
suggestions: []
};
}
onChange = (event, { newValue }) => {
this.setState({
value: newValue
});
};
onSuggestionsFetchRequested = ({ value }) => {
this.setState({
suggestions: getSuggestions(value)
});
};
onSuggestionsClearRequested = () => {
this.setState({
suggestions: []
});
};
render(){
const { value, suggestions } = this.state;
// Autosuggest will pass through all these props to the input.
const inputProps = {
placeholder: 'Search City...',
value,
onChange: this.onChange
};
return (
<div>
<Autosuggest
suggestions={suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
getSuggestionValue={getSuggestionValue}
renderSuggestion={renderSuggestion}
inputProps={inputProps}
/>
<br/>
</div>
)
}
}
export default Form;
I want to render only 5 items, otherwise, computer hangs while loading huge data. Is there any other autocomplete react npm package, since I want only cities and country list. i.e when city is inputted, automatically the city name must be suggested with its relevant country.Any solution or suggestion highly appreciated. Thanks in advance
i modified you're getSuggestions() method a little i guess this should work for you.
const getSuggestions = value => {
const inputValue = value.trim().toLowerCase();
const inputLength = inputValue.length;
// Here I get data from cities.json
return inputLength === 0 ? [] : cities.filter(lang =>
lang.name.toLowerCase().slice(0, inputLength) === inputValue
).slice(0,5);
};
Use the Slice method with start index and last Index
suggestions={suggestions.slice(0, 5)}
import {
React
,Avatar
,axiosbase
} from '../../import-files';
import Autosuggest from 'react-autosuggest';
import './autosuggest.css';
import { withStyles } from '#material-ui/core/styles';
import TextField from '#material-ui/core/TextField';
import Paper from '#material-ui/core/Paper';
import MenuItem from '#material-ui/core/MenuItem';
let suggestions = [ { label: 'Afghanistan' } ];
function renderInputComponent(inputProps) {
const { classes, inputRef = () => {}, ref, ...other } = inputProps;
return (
<TextField
className={classes.textField}
fullWidth
variant="outlined"
InputProps={{
inputRef: node => {
ref(node);
inputRef(node);
},
classes: {
input: classes.input,
},
}}
{...other}
/>
);
}
function renderSuggestion(suggestion, { query, isHighlighted }) {
return (
<MenuItem selected={isHighlighted} component="div">
<div>
<strong key={String(suggestion.id)} style={{ fontWeight: 300 }}>
<span className="sugg-option">
<span className="icon-wrap">
<Avatar src={suggestion.Poster}></Avatar>
</span>
<span className="name">
{suggestion.Title}
</span>
</span>
</strong>
</div>
</MenuItem>
);
}
function initSuggestions(value) {
suggestions = value;
}
function getSuggestionValue(suggestion) {
return suggestion.Title;
}
function onSuggestionSelected(event, { suggestion, suggestionValue, suggestionIndex, sectionIndex, method }) {
console.log('HandleSuggestion() '+suggestionValue);
}
const styles = theme => ({
root: {
height: 50,
flexGrow: 1,
},
container: {
position: 'relative',
},
suggestionsContainerOpen: {
position: 'absolute',
zIndex: 998,
marginTop: theme.spacing.unit,
left: 0,
right: 0,
overflowY: 'scroll',
maxHeight:'376%'
},
suggestion: {
display: 'block',
},
suggestionsList: {
margin: 0,
padding: 0,
listStyleType: 'none',
},
divider: {
height: theme.spacing.unit * 2,
},
});
class IntegrationAutosuggest extends React.Component {
state = {
single: '',
popper: '',
suggestions: [],
};
componentDidMount() {
initSuggestions(suggestions);
}
// Filter logic
getSuggestions = async (value) => {
const inputValue = value.trim().toLowerCase();
var _filter = JSON.stringify({
filter : inputValue,
});
return await axiosbase.post(`${apiCall}`, _filter);
};
handleSuggestionsFetchRequested = ({ value }) => {
this.getSuggestions(value)
.then(data => {
if (data.Error) {
this.setState({
suggestions: []
});
} else {
const responseData = [];
data.data.itemsList.map((item, i) => {
let File = {
id: item.idEnc,
Title: item.englishFullName +' '+item.arabicFullName,
englishFullName: item.englishFullName,
arabicFullName: item.arabicFullName,
Poster: item.photoPath,
}
responseData.push(File);
});
this.setState({
suggestions: responseData
});
}
})
};
handleSuggestionsClearRequested = () => {
this.setState({
suggestions: [],
});
};
handleChange = name => (event, { newValue }) => {
this.setState({
[name]: newValue,
});
if(event.type=='click'){
if(typeof this.props.handleOrderUserFirstNameChange === "function"){
this.props.handleOrderUserFirstNameChange(newValue);
}
this.state.suggestions.filter(f=>f.Title===newValue).map((item, i) => {
//id
//Title
// Poster
if(typeof this.props.handleUserIDChange === "function"){
this.props.handleUserIDChange(item.id);
}
});
}
};
render() {
const { classes } = this.props;
// console.log('Re-render!!');
// console.log(this.props);
// console.log(this.state.suggestions);
const autosuggestProps = {
renderInputComponent,
suggestions: this.state.suggestions,
onSuggestionsFetchRequested: this.handleSuggestionsFetchRequested,
onSuggestionsClearRequested: this.handleSuggestionsClearRequested,
onSuggestionSelected: this.props.onSelect,
getSuggestionValue,
renderSuggestion,
};
return (
<div className={classes.root}>
<Autosuggest
{...autosuggestProps}
inputProps={{
classes,
placeholder: this.props.placeHolder,
value: this.state.single,
onChange: this.handleChange('single'),
}}
theme={{
container: classes.container,
suggestionsContainerOpen: classes.suggestionsContainerOpen,
suggestionsList: classes.suggestionsList,
suggestion: classes.suggestion,
}}
renderSuggestionsContainer={options => (
<Paper {...options.containerProps} square>
{options.children}
</Paper>
)}
/>
<div className={classes.divider} />
</div>
);
}
}
export default withStyles(styles)(IntegrationAutosuggest);

React passing data from child component to parent component

I am using a material UI Auto-suggest component and i would like to pass the full name to the parent component. This link is similar to my code https://codesandbox.io/s/ryn76v485m
The parent component is passing down the emailUser props
<SearchForUsers emailUser={this.emailUsers}/>
emailUsers = (user) => {
debugger
console.log(user + "trying to pass down from child")
}
The problem that i am having is that i cannot get the child component to pass the state correctly to the parent component.
At the moment i am doing the this.props.emailUser(this.state.values) after the mapping of the employees. The state is only change after the second person has been entered. I tried putting the this.props.emailUser into the onChange but that event does not update the state when the user clicks on the suggested name. Can anyone tell me how to do get the state back to the parent component correctly.
This is my child component.
class ShareForUsers extends Component {
constructor(props){
super(props);
this.state = {
menuOpen: false,
value: "",
values: []
};
}
componentDidMount() {
if (!!this.props.employees && this.props.employees.length == 0) {
this.props.listEmployees();
}
}
componentWillReceiveProps(nextProps) {
this.setState({ ...nextProps })
}
render() {
return (
<div>
<TextField
fullWidth
value={this.state.value}
InputProps={{
startAdornment: this.state.values
.concat()
.sort(({ label: aLabel }, { label: bLabel }) => {
if (aLabel < bLabel) return -1;
else if (aLabel > bLabel) return 1;
return 0;
})
.map(chip => (
<InputAdornment
component={Chip}
label={chip}
onDelete={() => {
const value = chip;
this.setState(({ values: prevValues }) => {
const values = prevValues;
const idx = values.indexOf(value);
if (idx === -1) {
values.push(value);
} else {
values.splice(idx, 1);
}
return {
values
};
});
}}
/>
))
}}
onChange={evt => {
const value = evt.target.value;
this.setState({
value,
menuOpen: value.length > 0
});
}}
onFocus={() =>
this.setState(({ value }) => ({
menuOpen: value.length > 0
}))
}
onBlur={() => this.setState({})}
/>
<div>
{this.state.menuOpen ? (
<Paper
style={{
position: "absolute",
zIndex: 100,
width: "100%"
}}
>
{this.props.employees
.filter(
employee =>
employee.user.email.toLowerCase().indexOf(this.state.value) > -1
)
.map(employee => (
<MenuItem
key={employee.user.id}
onClick={() => {
this.setState(({ values: prevValues }) => {
const values = prevValues.concat();
const idx = values.indexOf(employee.user.id);
if (idx === -1) {
values.push(employee.user.email);
} else {
values.splice(idx, 1);
}
return {
values,
value: "",
menuOpen: false
};
});
}}
>
{employee.user.email}
</MenuItem>
))}
</Paper>
) : (
""
)}
</div>
</div>
)
}
}
const shareForUsers = withStyles(styles)(ShareForUsers)
export default connect(
state => state.user,
dispatch => bindActionCreators(actionCreators, dispatch)
)(shareForUsers);
Thanks
In onChange event you can pass the value from SearchForUsers component to its parent by this:
onChange={evt => {
const value = evt.target.value;
this.setState({
value,
menuOpen: value.length > 0
});
this.props.emailUser(value);
}}

Resources