how to re render my component when select value changes? - javascript

right now I have a map component which plots polyline data based on the value selected from my select menu component right now. The issue I am having is that when I change the value to a different option in my select menu and click the button to play the animation (note the button plays and pauses so will need to click until new play again) again it seems to still have the previous data stored within and so will plot one polyline for previous option chosen then one polyline for the new data selected. Where as I want it so that when the select menu value is changed the data is cleared and just stores the current selected and then will plot only the value selected. I cannot seem to figure it out been at this for a while now would appreciate some help. I want replayMap to be re rendered when select value changes
I have created a sandbox to run the program. There may be some CSS issues with the map seems patchy not sure why that is as it works on my application. I would appreciated if anyone can solve my issue mentioned with the select menus
link to code: https://codesandbox.io/s/distracted-sun-jwk3dg?file=/src/ReplayMap.js

If you want to render after you choise you can add key your component like the below.
import React, { useContext, useState, useEffect, useRef } from "react";
import {
MapContainer,
TileLayer,
Popup,
Polyline,
Marker
} from "react-leaflet";
import MovingMarker from "./MovingMarker";
import ActivityList from "./ActivityList";
let setPositionInterval;
function ReplayMap(props) {
const [mapRef, setMapRef] = useState(1); // added this line for trigger counter
const [nextPosition, setNextPosition] = useState();
const currentIdxRef = useRef(0);
const duration = 0.2 * 1000; // 1 second is 1000ms
useEffect(() => {
if (props.playStatus) {
setPositionInterval = setInterval(() => {
currentIdxRef.current < props.activityData.length &&
setNextPosition(props.activityData[currentIdxRef.current]);
setMapRef(mapRef++); // added this line if position change your counter increse and force re-render your component.
currentIdxRef.current = currentIdxRef.current + 1;
}, duration);
return () => {
clearInterval(setPositionInterval);
};
}
}, [props.playStatus]);
//console.log("Props = ", props.activityData);
return (
<div>
<MapContainer
center={[-8.798064, 115.222211]}
zoom={15}
scrollWheelZoom={false}
style={{ width: "400px", height: "400px" }}
key={mapRef.toString()}
>
<TileLayer
attribution='© OpenStreetMap contributors'
url={"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"}
/>
{props.playStatus && (
<MovingMarker
nextPosition={nextPosition ? nextPosition : props.activityData[0]}
duration={duration}
playStatus={props.playStatus}
/>
)}
</MapContainer>
</div>
);
}
export default ReplayMap;

Related

REACT connect 4 game - attempt to reset grid over setState but results in "undefined"

I am trying to create a Connect of 4 game in React as an exercise.
If i want to reset the grid or for displaying player points, a reset of my grid is required rather than simply reloading the entire page.
In this case, dealing with my grid via state is a logical step, but after several attempts and variations, I'm unfortunately lost at the moment
In this variation below, this.state.grid always returns undefined on reset (console.log right after render method begins).
I see that the problem is most likely because in the gridHtml function I am already passing the grid to the state via setState.
If I call this.gridHTML() directly on the reset button, my grid completely disappears.
I am very grateful for any help at this point
import React from 'react';
class Grid extends React.Component {
constructor(props) {
super(props);
this.state = {
player: "red",
isGameOver: false,
gamestarts: false
};
this.findLastEmptyColl = this.findLastEmptyColl.bind(this);
this.onMouseEnter = this.onMouseEnter.bind(this);
this.onMouseLeave = this.onMouseLeave.bind(this);
this.onClick = this.onClick.bind(this);
this.checkForWinner = this.checkForWinner.bind(this);
this.gridHtml = this.gridHtml.bind(this);
this.reset = this.reset.bind(this);
};
/*left out MouseEnter, leave, click and win logic , those work fine and to keep it short */
gridHtml() {
let rows = Array(6).fill(0), cols = Array(7).fill(0);
let grid = rows.map((el, i) => {
return (
<div key={i} className="row">
{cols.map((value, index) => {
return (
<div key={index}
onMouseEnter={this.onMouseEnter}
onMouseLeave={this.onMouseLeave}
onClick={this.onClick}
className="col empty"
data-col={index}
data-row={i}>
</div>
);
})}
</div>
);
});
this.setState({
grid: grid
});
}
componentDidMount() {
this.gridHtml();
}
componentWillMount() {
this.gridHtml();
}
reset() {
this.setState({
grid: this.gridHtml(),
isGameOver: false,
gamestarts: false
})
}
render() {
console.log(this.state.grid);
return (
<>
{!this.state.gamestarts && <h4>Connect 4 - Player {this.state.player} begins!</h4>}
{this.state.gamestarts && <h4>Player {this.state.player} </h4>}
{(this.state.isGameOver && !this.state.gamestarts) && <h4>Player {this.state.player} has won</h4>}
<div id="board">
{this.state.grid}
</div>
<div>
<button style={{margin: "30px"}} onClick={() => {this.reset()}}>Reset</button>
</div>
</>
)
}
}
export default Grid;
Update:
I see that my understanding of React doesn't seem to be properly adjusted yet; in my reset() function, due to the asynchronicity of react, I assume that the dynamic assignment via setState of my grid should actually render automatically?
Again, the problem: when I currently press my reset button, the grid is re-created but the moves, red and yellow, are still on the grid as they were; last I thought of writing a function that instead of creating a new grid removes all CSS classes and data properties related to it - but that would make the whole point of doing something like this with React absurd.
To make it even clearer:
if I extend my reset() function with a setTimeout around setState, right after overwriting my grid, it works?! I can understand why but this right now feels like a hack and I don't want to leave it like this, because this is supposed to be the core competence of React? Hope it helps to understand better
reset () {
this.setState({grid: 'some text ... loading '});
setTimeout(() =>{
this.setState({
grid: this.gridHtml(),
isGameOver: false,
gamestarts: true,
player: "red"
});
}, 1000);
}
Hope somebody can explain?
Many thanks
Your gridHtml() function doesn't return anything so grid is being set to undefined. Try adding a return grid; statement to the end.

Lags in caret display (caret does not reset on position change)

I'm using Prismjs in my React app and everything's working fine. I just have a caret display lag whenever I move it using my keyboard arrows. In standard inputs/textareas, it looks like the caret sort of "resets" its cycle when moved. But here, it just keeps on blinking at the same pace. So if you press left arrow four times in a row, you'll have to wait until the caret appears again to see your current position (which lasts something like 0.5 second). Which is kind of disturbing.
Here's my code if it helps :
import { useState, useEffect } from "react";
import Prism from "prismjs";
export default function EditCode() {
const [content, setContent] = useState("");
const handleKeyDown = (e) => {
let value = content,
selStartPos = e.currentTarget.selectionStart;
if (e.key === "Tab") {
value =
value.substring(0, selStartPos) +
" " +
value.substring(selStartPos, value.length);
e.currentTarget.selectionStart = selStartPos + 3;
e.currentTarget.selectionEnd = selStartPos + 4;
e.preventDefault();
setContent(value);
}
};
useEffect(() => {
Prism.highlightAll();
}, []);
useEffect(() => {
Prism.highlightAll();
}, [content]);
return (
<section className="codeGlobalContainer">
<textarea
className="codeInput"
spellCheck="false"
wrap="off"
cols="200"
value={content}
onChange={(e) => setContent(e.target.value)}
onKeyDown={(e) => handleKeyDown(e)}
/>
<pre className="codeOutput">
<code className={`language-javascript`}>{content}</code>
</pre>
</section>
);
}
I get that there's no CSS property like caret-blinking-speed, but is there anything I could do ? Is the handleKeyDown() function the source of my problem ?
N.B. : if I select text in my textarea, using keyboard, everything's going smoothly. Likewise, if I move my cursor with my keyboard arrows and type text instantly, there's no problem neither. It's really just the display of the caret.
Edit : If I remove the pre bloc, the problem is solved and the caret returns to its basic "reset blinking cycle on move" status.
After several tests, I've found out that it's the spellCheck="false" line in my textarea that creates my problem. So it's solved for now... But I don't know why.

How to show marker location from localStorage in react leaflet

I am trying to show the saved marker location once the page is refreshed but so far no luck. The App is deployed here: https://master.d8pjybx1mf2s7.amplifyapp.com
It's a basic app to find if User 2 is in range of User 1. We can mark locations by clicking on the map.
handleClick(e){
this.setState({ currentPos: e.latlng });
localStorage.setItem('Poslat1', this.state.currentPos.lat);
localStorage.setItem('Poslon1', this.state.currentPos.lng);
}
render() {
return (
<div>
<Map center={this.props.center} zoom={this.props.zoom} onClick={this.handleClick}>
<TileLayer
url='https://{s}.tile.osm.org/{z}/{x}/{y}.png'
/>
{ this.state.currentPos && <MyMarker icon={LocationIcon} position={this.state.currentPos}>
</MyMarker>}
{this.state.currentPos && <Circle center={this.state.currentPos} pathOptions={{fillColor: 'blue' }} radius={1000} />}
</Map>
</div>
);
}
I am able to store the location data by using localStorage which is used to find range, I thought I could same method to somehow save the marker location on map as well but it's not working.
Any help is appreciated. Thank You
When the component mounts check if lat1 and lon1 exist in your localStorage and if they do change the component state
componentDidMount() {
const lat1 = localStorage.getItem("Poslat1");
const lon1 = localStorage.getItem("Poslon1");
if (lat1 && lon1) this.setState({ currentPos: [lat1, lon1] });
}
then upon refresh they will be persisted.
Demo

why tooltip is not hide on mouseleave event in react?

I am trying to show tooltip on mouse enter and hide on mouse leave.first i make a simple demo which is working fine.
https://codesandbox.io/s/exciting-shannon-4zuij?file=/src/list.js
above code working fine on hover it show's the tooltip and hide on leave.
see same concept i apply on a application.(this code is not working)
https://codesandbox.io/s/cool-liskov-8rvjw?file=/src/App.js
when I hover a item is show the tooltip.but it is not hiding the tooltip when you leaving the item.something went wrong.
const Student = ({students,clickHandler}) => {
console.log(students,"---ee")
const [selectedStudent,setSelectedStudent] = useState(null)
const onMouseHandler = (student,e)=>{
student.visibility = true
setSelectedStudent(student)
}
const onMouseLeaveHandler = (student)=>{
console.log('======',student)
student.visibility = false
setSelectedStudent(student)
}
return (
<ul className="student-container">
{
students && students.length > 0 ? students.map((student,index)=>{
return (
<li key={index} onClick={()=>{
clickHandler(student)
}}
onMouseLeave={()=>{
onMouseLeaveHandler(student)
}}
onMouseEnter={(e)=>{
onMouseHandler(student,e)
}} style={{position:'relative'}}>
<a><span>{student.name}</span></a>
{student.visibility? <ToolTip showToolTip={student.visibility} selectedStudent={selectedStudent}/>:null}
</li>
)
}):null
}
</ul>
);
};
export default Student;
Step too reproduce
Hover on first item Raj
and then try to hover sameer.both tooltip will display.I want only one tooltip will be display which is hovered.
I want my handlers should be in my functional component . I don't want to move these handler to parent component and pass handler as a props
In your demo it's also not work well, - one hide only when open another.
when you set student.visibility you not set state, so nothing has rerendered.
Then when you call setSelectedStudent you pass there just the same referance as was before, since it's the same object, so the state not changed, and again - nothing got rerendered.
What you have to do is pass the updated student in a new variable. like so:
setSelectedStudent({...student})
Then all should work

Targeting only the clicked item on a mapped component ( React quiz trivia App)

i'm trying to develop an App with React using the Open trivia Api. I have mapped a button component (using material ui) to show the different answers for each question. I'm struggling now to target only the clicked one to apply a css property: if the answer is correct should become green, else red. The problem is the fact that once i click, all button become red or green. I tried to store the index in a state and compare the real index, but it doesn't work. here is my code:
in the main APP.js
const [clickedOne, setClickedOne] = useState({
clickedIndex: null,
});
useEffect(() => {
grabData();
}, []);
const handleClick = (choice, ke) => {
setChoice(choice);
if (choice === data.correct_answer) {
setIsCorrect(true);
} else {
setIsCorrect(false);
}
setClickedOne({ clickedIndex: ke });
grabData();
};
The mapped button inside the Render:
{answers.map((answer, index) => {
return (
<ContainedButtons
choice={handleClick}
answer={answer}
correct={data.correct_answer}
isCorrect={isCorrect}
key={index}
id={index}
clicked={clickedOne}
/>
);
})}
Inside the Button component:
const backStyle = () => {
if (clicked === id) {
if (isCorrect) {
return "green";
} else if (isCorrect === false) {
return "red";
} else {
return null;
}
}
};
return (
<div className={classes.root}>
<Button
style={{ backgroundColor: backStyle() }}
value={answer}
onClick={() => choice(answer, id)}
variant="contained"
>
{decodeURIComponent(answer)}
</Button>
When i check now inside the backstyle function if the clicked===id, now nothing happens anymore. Without that if check, i would have all buttons red or green.
Thank you guys for the help!
I have looked at your codesandbox demo, there are alot of other problems apart from the one your question is about.
First of all, each time you make a request to the API to fetch next question, you are making a request to get 10 questions instead of 1. API request URL contains a query parameter named amount which determines how many questions will be fetched on each request. Change its value to 1.
"https://opentdb.com/api.php?amount=1&encode=url3986"
Secondly, there is a lot of unnecessary code and unnecessary use of useState hook. You only need 2 things to be stored in the state, data and answers
const [data, setData] = useState({});
const [answers, setAnswers] = useState([]);
Now, coming to the original problem of detecting which button is clicked and correctly updating its background color.
To achieve the desired functionality, take following steps:
create couple of CSS classes as shown below
button.bgGreen {
background-color: green !important;
}
button.bgRed {
background-color: red !important;
}
pass a handleClick function from App component to ContainedButtons component. When a button is clicked, this click handler will be invoked. Inside the handleClick function, get the text and the button that was clicked using Event.target and depending on whether user answered correctly or not, add appropriate CSS class, created in step 1, on the button that was clicked.
Instead of using index as key for ContainedButtons in map function, use something that will be unique each time. This is needed because we want React to not re-use the ContainedButtons because if React re-uses the ContainedButtons component, then CSS classes added in step 2 will not be removed from the button.
Here's a working codesanbox demo of your app with the above mentioned steps.
In this demo, i have removed the unnecessary code and also changed the key of ContainedButtons inside map function to key={answer.length * Math.random() * 100}. You can change it to anything that will ensure that this key will be unique each time.

Categories

Resources