How render two different contents of a modal window? - javascript

Good afternoon, I have a modal window in which the content is displayed depending on the condition.
I can't to do when the subscription is disabled, the content of the modal changed to "Subscription successful disabled".
And when you click on the "Enable subscription" button, the content of the modal changed to "Enable subscription by clicking on the button"
Now it turns out that "Subscription successful disabled" and "Enable subscription by clicking on the button" are displayed at once.
The enableSub and disableSub functions emulate sending data to the server, and the state emulates receiving data from the server.
const {
useState
} = React;
const state = {
isSubscriptionEnabled: true,
}
const enableSub = () => {
state.isSubscriptionEnabled = true
}
const disableSub = () => {
state.isSubscriptionEnabled = false
}
function App() {
const [isOpen, setIsOpen] = useState(false)
return ( <
div > {
isOpen &&
< div className = "modalWrapper" >
<
div className = "modal" >
<
button onClick = {
() => setIsOpen(false)
} > close < /button> <
div > {
state.isSubscriptionEnabled &&
< div >
Are you sure disable subscription ?
<
button className = "button"
onClick = {
disableSub
} >
Disalbe subscription <
/button> <
/div>
} {
!state.isSubscriptionEnabled &&
< div >
Subscription successful disabled <
button onClick = {
() => setIsOpen(false)
} > Thanks < /button> <
/div>
} {
!state.isSubscriptionEnabled &&
< div >
Enable subscription by clicking on button <
button className = "button"
onClick = {
enableSub
} >
Enable subscription <
/button> <
/div>
} <
/div> <
/div> <
/div>
}
{
state.isSubscriptionEnabled &&
< button className = "button"
onClick = {
() => setIsOpen(true)
} >
Disalbe subscription <
/button>
} {
!state.isSubscriptionEnabled &&
< button className = "button"
onClick = {
() => setIsOpen(true)
} >
Enable subscription <
/button>
} <
/div>
)
}
ReactDOM.render( < App / > , document.getElementById('app'));
.app {
position: relative;
}
.button {
margin: 20px;
}
.modalWrapper {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
background: #ccc;
display: flex;
align-items: center;
justify-content: center;
}
.modal {
width: 300px;
height: 150px;
padding: 30px;
position: absolute;
border: 1px solid teal;
background: #fff;
}
<div id="app"></div>
jsfiddle

import {React,useState} from 'react'
function App() {
const [isOpen, setIsOpen] = useState(false);
const [isSubscriptionEnabled, enableSubscription] = useState(false)
const enableSub = () => {
enableSubscription(true)
}
const disableSub = () => {
enableSubscription(false)
}
return (
<div> {
isOpen &&
<div className = "modalWrapper" >
<div className = "modal" >
<button onClick = {
() => setIsOpen(false)
} > close </button>
<div> {
isSubscriptionEnabled&&
<div>
Are you sure disable subscription ?
<button className = "button"
onClick = {
disableSub
} >
Disable subscription
</button>
</div>
} {
!isSubscriptionEnabled&&
<div>
Subscription successful disabled
<button onClick = {
() => setIsOpen(false)
} > Thanks </button>
</div>
} {
!isSubscriptionEnabled&&
<div>
Enable subscription by clicking on button
<button className = "button"
onClick = {
enableSub
} >
Enable subscription
</button>
</div>
} </div>
</div>
</div>
}
{
isSubscriptionEnabled&&
<button className = "button"
onClick = {
() => setIsOpen(true)
} >
Disable subscription
</button>
} {
!isSubscriptionEnabled&&
<button className = "button"
onClick = {
() => setIsOpen(true)
} >
Enable subscription
</button>
}
</div>
)
}
ReactDOM.render( < App / > , document.getElementById('app'));
React components only render when we change a component using its useState inside that particular component. it will not rerender on your global variable
Please try to format your code at least before posting here,

Related

Count using useState doesnt update state until question 6 even when answered correctly react

im trying to create a quiz app in react using hooks. i want the answers to be clickable and once clicked the user will then move onto the next question. My problem is that, the score state isnt updating until question 6! i am making an API call to get the questions and answers using useEffect. i know that useState is asynchronous and thats why state doesnt update straight away, its just even if i answer the first 6 questions correctly, after question 6 the score is still showing 1. Does anyone have a way around this?
My API call using useEffect:
useEffect(() => {
axios.get("https://my-quiz-server.herokuapp.com/api/newq").then((res) => {
const allQuestions = res.data;
setResult([allQuestions]);
})
.catch((error) => {
console.log(error);
});
}, [setResult]);
My Quiz component:
import React, {
useState,
useContext,
useEffect
} from "react";
import {
QuizContext
} from "../Helpers/context";
const MainQuiz = () => {
const {
score,
setScore,
result
} = useContext(QuizContext);
const [currentQuestion, setCurrentQuestion] = useState(0);
const [optionChosen, setOptionChosen] = useState("");
console.log(optionChosen);
console.log(result);
const nextQuestion = (correctAnswer) => {
if (optionChosen === correctAnswer) {
setScore((score) => score + 1);
}
setCurrentQuestion((currentQuestion) => currentQuestion + 1);
};
useEffect(() => {
console.log("score updated", score);
}, [score]);
return ( <
div className = "quiz" > {
result.map((question, index) => {
if (currentQuestion < question.allQuiz.length) {
return ( <
h3 key = {
index
} > {
question.allQuiz[currentQuestion].q_prompt
} < /h3>
);
} else {
return null;
}
})
} <
div className = "answer__container" > {
result.map((answers, index) => {
if (currentQuestion < answers.allQuiz.length) {
return ( <
div className = "answer__options" > {
" "
} <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("a");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].a
} <
/button> <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("b");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].b
} <
/button> <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("c");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].c
} <
/button> <
button className = "question__choices"
onClick = {
() => {
setOptionChosen("d");
nextQuestion(
result[0].allQuiz[currentQuestion].q_correctAnswer
);
}
} >
{
answers.allQuiz[currentQuestion].d
} <
/button> <
/div>
);
} else {
return null;
}
})
} <
/div> <
span > {
score
} < /span> <
/div>
);
};
export default MainQuiz;

Using jQuery in React to modify CSS of an element

Hello,
I know its not recommended to use jQuery with react & I am aware of method on react for changing CSS of element but here I am just trying to see if my req can be achieved or not , all i want is to change the colour of li element when corresponding tick icon is clicked for it, I am using a jQuery code
const markdone = () => {
let c = $("#ll")
console.log(c)
$(c).closest("li").css("background-color", "green");
};
but when i am clicking the css gets applied but not on its corresponding li element in my case for ex have attached image when i click on 3 tick icon css gets changed for 1 is there any way i can fix it
attaching whole code below
check markdone function for making css change :
const [input, setValue] = useState("");
const [todos, setTodos] = useState([]);
// passing entered
const handleInput = (event) => {
setValue(event.target.value);
};
const lp = (event) => {
// let c = [1,2,34,55]
event.preventDefault();
// if no input nothing will happen return none
if (!input) return;
// using spread operator its used whenever we need to add array data to exiting array
const newTodos = [...todos, input];
setTodos(newTodos);
// clearing input text
setValue("");
};
const handeldel = (index) => {
// console.log(index)
todos.splice(index, 1);
setTodos([...todos]);
// const newTodos = todos.splice(index, 1);
// setTodos([...newTodos]);
};
const [line, setline] = useState(false);
// const [ll, setll] = useState(false);
const markdone = () => {
let c = $("#ll")
console.log(c)
$(c).closest("li").css("background-color", "green");
};
useEffect(() => {
$(document).ready(function() {
$("#pk").click(function(e) {
// e.preventDefault();
alert('hello')
});
});
});
return ( <
div >
<
h1 id = "pk"
className = "text-center font-weight-bolder alert-info mb-5" >
Tasks To Do < i class = "fas fa-clipboard-list text-success" > < /i> <
/h1> <
div class = "input-group mb-3 container" >
<
input className = "form-control border-primary font-weight-bold"
style = {
{
height: 60
}
}
placeholder = "Enter Text here"
type = "text"
value = {
input
}
onChange = {
handleInput
}
/> <
div class = "input-group-append" >
<
button className = "input-group-append font-weight-bolder "
style = {
{
fontSize: 20
}
}
onClick = {
lp
} >
{
" "
} <
i class = "fas fa-plus-square fa-2x p-2" > < /i>{" "} <
/button> <
/div> <
/div> {
todos.map((x, index) => ( <
ol style = {
{
listStyle: "outside"
}
}
className = "container" >
<
li className = "font-weight-bolder table-bordered text-capitalize alert-secondary "
style = {
{
fontSize: 30,
textDecoration: line ? "line-through" : "none",
// backgroundColor: ll ? "Chartreuse" : "none",
}
} >
{
x
} <
i class = "fas fa-check-circle float-md-right text-success"
id = "ll"
onClick = {
markdone
} >
< /i>{" "} <
i class = "fas fa-trash-alt text-danger float-md-right"
onClick = {
() => handeldel(index)
} >
< /i> <
/li> <
/ol>
))
}
{ /* for future ref */ } {
/* <div >
{data.map((user) => (
<div className="user">{user.id + " " + user.name
}</div>
))}
</div> */
} <
/div>
I suppose using a Ref should do the trick, as Refs provide a way to access DOM nodes or React elements created in the render method.
Just put it on the element you'd like to style using jQuery and access it with RefName.current
IDs must be unique
You do not need jQuery, just delegation
Plain JS - there are other ways in React
I am assuming .input-group-append is the container
document.querySelector(".input-group-append").addEventListener("click",function(e) {
const tgt = e.target;
if (tgt.classList.contains("text-success")) {
tgt.closest("li").style.backgroundColor = "green";
}
})

Using Use Effect with hooks

I am trying to create a demo weather fetching app , I need to display weather data of city name as typed , can it be achieved with us effect or there is any other approach for the same
also I need to display results only if data exits currently in my code if data is not available .. default state data is showing kindly suggest to fix both issues
import React, {
useState,
useEffect
} from "react";
import Clock from "react-live-clock";
import "./Weather.css";
import {
ToastContainer,
toast
} from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import {
API_KEY
} from "./Api";
export default function Weather() {
const [city, setcity] = useState("goa");
const [currentweather, setcurrentweather] = useState("");
const [icon, seticon] = useState("");
const [weathertext, setweathertext] = useState("")
const lpsearch = () => {
const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric`;
if (city) {
fetch(url)
.then((res) => res.json())
.then((data) => {
if (data.cod === "404") {
// console.log("City Not found");
toast.error(`Entered City-Name ${city} Not Found `, {
position: "top-center",
autoClose: 2000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
});
setcity("");
} else {
console.log(data);
setcurrentweather(data.main.temp);
setweathertext(data.weather[0].description)
seticon(data.weather[0].icon);
setcity("");
}
// setcity("");
});
}
};
let inputhndl = (event) => {
setcity(event.target.value);
};
return ( <
div >
<
ToastContainer position = "top-center"
autoClose = {
2000
}
hideProgressBar = {
false
}
newestOnTop = {
false
}
closeOnClick rtl = {
false
}
pauseOnFocusLoss draggable pauseOnHover /
> {
" "
} <
div className = "container-fluid" > {
" "
} <
h2 className = "text-center alert-success font-weight-bolder " >
<
Clock format = {
"dddd, DD MMMM , YYYY, h:mm:ss"
}
ticking = {
true
}
/> <
/h2> <
/div> <
div className = "lpcentered " >
<
h3 className = "alert-info text-capitalize text-center" >
Enter City 's <i className="fas fa-city text-secondary"></i> Name <
/h3> <
input onChange = {
inputhndl
}
value = {
city
}
placeholder = "Enter Here"
className = "form-control btn-outline-warning font-weight-bolder mb-3 mt-3" >
< /input> <
button className = "form-control btn-success font-weight-bold "
onClick = {
lpsearch
} >
Search < i className = "fas fa-search " > < /i> <
/button> <
div className = "text-center" >
<
h3 className = "alert-secondary text-capitalize" > {
city
} < /h3>
<
h3 className = "text-capitalize alert-info" > {
city
} < i class = "fas fa-temperature-low" > < /i> - {currentweather} <span>℃</span > < /h3> <
h3 > {
weathertext
} < img src = {
`http://openweathermap.org/img/wn/${icon}#2x.png`
}
alt = "icon"
srcset = "" /
> < /h3>
<
/div> <
/div> <
/div>
);
}

React Portal input change removes elements

I've created this pen to demo it: https://codepen.io/no1melman/pen/WWyJqQ
essentially I have this portal:
const ChatArea = ({ children }) => {
const el = document.createElement("div");
el.classList.add('chatbox')
const root = document.getElementById("rootus");
useEffect(() => {
root.appendChild(el);
return () => root.removeChild(el);
}, []);
return createPortal(children, el);
};
And Use like:
const ChatBox = () => {
const [ reply, setReply ] = useState('');
const handleReply = (e) => {
e.preventDefault();
setReply(e.target.value);
}
return (
<ChatArea>
<div className="chat-title">Bot Convo</div>
<div className="chat-convo"></div>
<div className="chat-reply">
<input type="text" value={reply} onChange={handleReply} />
<button>Send</button>
</div>
</ChatArea>
)
}
For some reason as you start typing the body of the chatbox seems to disappear... I've put logs into the useEffect to see if that was causing it, but it didn't show what I thought
There are two issues here:
The first issue is
useEffect(() => {
root.appendChild(el);
return () => root.removeChild(el);
}, []);
Now as per the hooks principle the dependency should match the used variables inside the hook. If not used react will not run the effect next time.
SO in your case when you click on open chat it opens up the chat box. the effect ran and rendered the portal with the input box.
When you have typed the first letter and onChange happened
It triggered the rerender of ChatArea, which should have ideally run the effect again, but didn't run as dependency array is blank and react has not idea as to when re-render.so the effect ran once for the first time where chatArea ran UI mounted and next time, the effect did not run as dependency array is blank.
This line :
return createPortal(children, el); // is referring to the new el which is created
but not attached to DOM. Hence nothing is visible on UI inside chatbox.
Refer this link: do not miss dependencies React hooks FAQs sections they are great :)
2nd issue:
Ideally, new div should not be created every time. Persist the "div" element across consecutive rerenders
See this implementation: I know there can be other ways of implementing it.
Feedbacks are welcome.
const {
render,
createPortal
} = ReactDOM;
const {
useState,
useEffect,
useRef
} = React;
const ChatArea = ({
children
}) => {
const el = document.createElement("div");
el.classList.add('chatbox')
// This el above will be different in each render
// root will remain same, ideally root and chatdiv should be passed as props
const root = document.getElementById("rootus");
// this val and setVal is done to toggle render the chart area after
// chatDiv is updated
const [val, setVal] = useState(true)
const chatDiv = useRef(null)
// First useEffect to persist the div
useEffect(() => {
if (!chatDiv.current) {
chatDiv.current = el
setVal(!val)
}
}, [chatDiv])
useEffect(() => {
root.appendChild(chatDiv.current);
return () => {
return root.removeChild(chatDiv.current)
}; // you are removing it
}, [chatDiv, root]);
if (chatDiv.current) {
return createPortal(children, chatDiv.current)
}
return null
// In your case as the return happened first and found out the el
};
const ChatBox = () => {
const [reply, setReply] = useState('');
const handleReply = (e) => {
e.preventDefault();
setReply(e.target.value);
}
return ( <
ChatArea >
<
div className = "chat-title" > Bot Convo < /div>
<
div className = "chat-convo" > < /div>
<
div className = "chat-reply" >
<
input type = "text"
value = {
reply
}
onChange = {
handleReply
}
/> <
button > Send < /button> <
/div> <
/ChatArea>
)
}
const NavBar = ({}) => ( <
div className = "navbar" >
<
div > Home < /div> <
div > Somewhere < /div> <
/div>
);
const Main = () => {
const [showChat, setShowChat] = useState(false);
const openChat = () => {
setShowChat(true);
};
const chatterbox = showChat ? ( < ChatBox / > ) : null;
return ( <
div className = "container" >
<
h2 > Main < /h2> <
p >
It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.The point of
using Lorem Ipsum is that it has a more - or - less normal distribution of
letters, as opposed to using 'Content here, content here', making it look like readable English.Many desktop publishing packages and web page editors now use Lorem Ipsum as their
default model text, and a search
for 'lorem ipsum'
will uncover many web sites still in their infancy.Various versions have evolved over the years, sometimes by accident, sometimes on purpose(injected humour and the like). <
/p> <
p style = {
{
display: "flex",
justifyContent: "center"
}
} >
<
button onClick = {
openChat
} > Open Chat < /button> <
/p> <
p style = {
{
display: "flex",
flexDirection: "column",
justifyContent: "center",
backgroundColor: "red"
}
} >
{
chatterbox
} < /p> <
/div>
);
};
const App = ({}) => ( <
div className = "app" >
<
NavBar / >
<
Main / >
<
/div>
);
render( < App / > , document.getElementById("rootus"));
body {
font-family: Raleway;
}
* {
box-sizing: border-box;
}
#rootus {
position: relative;
height: 100vh;
display: flex;
justify-content: center;
}
.navbar {
display: flex;
justify-content: center;
}
.navbar>div {
padding: 10px;
}
.navbar>div:hover {
background-color: gray;
cursor: pointer;
}
.container {
width: 960px;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
}
.chatbox {
width: 400px;
height: 200px;
position: absolute;
bottom: 0;
border: 2px solid black;
background: white;
display: flex;
flex-direction: column;
}
.chat-title {
background: black;
color: white;
}
.chat-convo {
flex: 1;
display: flex;
}
.chat-reply {
display: flex;
border-top: 1px solid black;
}
.chat-reply>input {
width: 80%;
padding: 8px;
border: none;
outline: none;
}
.chat-reply>button {
outline: none;
border: none;
flex: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="rootus">
</div>
Ui was not coming up proper in the stackoverflow code snippet, so I
had to edit somethings in styling. you can have a look at code pen
codepen linkaccording to your original styling

Why does not my onClick event work?

I used this.handleClick in onClick but it alerts a mistake.(this is not difined)
var Buttons = React.createClass({
getInitialState() {
return {
field: ':P '
}
},
handleClick(field) {
return this.setState = {
field
}
},
render() {
let count = 1;
return ( < div >
< h1 > {
this.state.field
} < /h1> {
buttonValues.map(function(val) {
return <button key = {
count++
}
onClick = {
Buttons.handleClick
} > {
val
} < /button>
})
} < /div>
)
}
})
ReactDOM.render( < Buttons / > , document.getElementById('app'));
You have to replace
this.setState = {field}
by
this.setState({field: field})
You cannot use Buttons.handleClick() directly because Buttons is a class. You need to instantiate the class before accessing it's method. And This won't work with React.
So, the best way is to use this to access the local methods within the class. And this.setState() is a function, to which you need to pass the new state object. Hope it helps!
<script src="https://unpkg.com/babel-core#5.8.38/browser.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
<script type="text/babel">
var buttonValues = ['hello','there']
var Buttons = React.createClass({
getInitialState() {
return {
field: ':P '
}
},
handleClick(field) {
this.setState({
field
})
},
render() {
var _this = this
return ( < div >
< h1 > {
this.state.field
} < /h1> {
buttonValues.map(function(val, i) {
return <button key = {
i
}
onClick = {() =>
_this.handleClick(val)
} > {
val
} < /button >
})
} < /div>
)
}
})
ReactDOM.render( < Buttons / > , document.getElementById('app'));
</script>
you are not passing any arguments to your function, also use this.handleClick instead of calling the class.
onClick = {this.handleClick.bind(null, val)}
also setState is a function
this.setState({field: field})

Categories