Why won't one of my components hide onclick using hooks? - javascript

For the life of me I can't figure out why the Spin Now won't hide upon clicking it. It rightfully shows Claim Now upon clicking Spin Now but once Claim Now shows, I want the Spin Now to hide. I'm using hooks, what am I doing wrong?
import React, { useState } from 'react';
import SpinNowButton from '../../components/SpinNowButton/SpinNowButton';
import ClaimNowButton from '../../components/ClaimNowButton/ClaimNowButton';
import './Buttons.css';
const Buttons = () => {
const [showSpin, setShowSpin] = useState(false);
const [showClaim, setShowClaim] = useState(false);
return(
<div className="both-buttons">
<SpinNowButton onClick={() => setShowClaim(true)}/>
{showClaim ? <ClaimNowButton/> : null}
{showSpin ? <SpinNowButton/> : null}
</div>
);
};
export default Buttons;

So you only need one piece of state to accomplish this, and you set it to the opposite of what the value was previously. Then the ternary components below will render one component if true, and the other if false.
Edit - I think this is closer to what you're looking for:
import React, { useState } from 'react';
import SpinNowButton from '../../components/SpinNowButton/SpinNowButton';
import ClaimNowButton from '../../components/ClaimNowButton/ClaimNowButton';
import './Buttons.css';
const Buttons = () => {
const [showClaim, setShowClaim] = useState(false);
const handleCLick = () => {setShowClaim(!showClaim)}
return(
<div className="both-buttons">
{showClaim ? null : <SpinNowButton onClick{() => handleClick()}/>}
{showClaim ? <ClaimNowButton onClick{() => handleClick()}/> : null}
</div>
);
};
export default Buttons;

An implementation which is flexible in adding more types such as claim/spin:
import React, { useState } from 'react';
import SpinNowButton from '../../components/SpinNowButton/SpinNowButton';
import ClaimNowButton from '../../components/ClaimNowButton/ClaimNowButton';
import './Buttons.css';
const Buttons = () => {
const [shownItems, setShownItems] = useState(['spin']);
return(
<div className="both-buttons">
{shownItems.contains('claim') && <ClaimNowButton onClick={() => setShownItems('spin')}/>}
{shownItems.contains('spin') && <SpinNowButton onClick={() => setShownItems('claim')}/>}
</div>
);
};
export default Buttons;

Related

React elements are removed on state update

I'm supposed to have a modal appear with an image in it. There are next and previous buttons which controls which image you are currently viewing. The modal is rendered in a portal. That in itself is working correctly. However, when I add children, and those childrens are updated, the modal only (not the portal) gets removed from the flow. In the React DevTools, the "isOpen" state of the modal is still set to true. I am using React 17.0.2 with NextJS 12.0.4 and Styled Components 5.3.3.
I have tried:
memoizing my components (as you can see there are some remnants of those trials) but this did not work
extracting the state of the modal to the parent and passing it as props and it didn't work either
I know there must be something wrong that I'm doing here so if you could help me find it that would be much appreciated!
Here is the controller where the modal is rendered:
import { FC, MouseEventHandler, useEffect, useState } from "react";
import { Photo } from "services/Images/Images.interfaces";
import { useGetNextPhoto, useGetPhotos, useGetPreviousPhoto } from "state";
import SlideshowContextProvider from "./Context/SlideshowContext";
import SlideShowModal from "./SlideShowModal";
const SlideshowController: FC = () => {
const photos = useGetPhotos();
const [currentlyViewedPhoto, setCurrentlyViewedPhoto] = useState<Photo | null>(null);
const nextPhoto = useGetNextPhoto(currentlyViewedPhoto?.id);
const previousPhoto = useGetPreviousPhoto(currentlyViewedPhoto?.id);
const onPreviousRequest: MouseEventHandler<HTMLButtonElement> = (event) => {
event.preventDefault();
setCurrentlyViewedPhoto(previousPhoto);
};
const onNextRequest: MouseEventHandler<HTMLButtonElement> = async (event) => {
event.preventDefault();
setCurrentlyViewedPhoto(nextPhoto);
};
useEffect(() => {
setCurrentlyViewedPhoto(photos[0]);
}, [photos]);
return (
<SlideshowContextProvider
currentlyViewing={currentlyViewedPhoto}
onNextSlideRequest={onNextRequest}
onPreviousSlideRequest={onPreviousRequest}
>
<SlideShowModal />
</SlideshowContextProvider>
);
};
export default SlideshowController;
The SlideshowModal:
import { Modal } from "components";
import { FC } from "react";
import SlideshowControlBar from "./SlideshowControlBar";
import SlideshowImage from "./SlideshowImage";
const SlideShowModal: FC = () => {
return (
<Modal uniqueKey="slideshow">
<SlideshowImage />
<SlideshowControlBar />
</Modal>
);
};
export default SlideShowModal;
The modal in itself:
import Portal from "components/Portal/Portal";
import { FC, useEffect, useMemo, useState } from "react";
import { useRegisterModal } from "state";
import styled from "styled-components";
import useWindowScrollLock from "./hook/UseWindowScrollLock";
interface Props {
uniqueKey: string;
isBackgroundOpaque?: boolean;
}
... Styled elements
const Modal: FC<Props> = ({ uniqueKey, isBackgroundOpaque = true, children }) => {
const [isOpen, setIsOpen] = useState(false);
const open = () => setIsOpen(true);
const close = () => setIsOpen(false);
const register = useRegisterModal(uniqueKey);
const isModalOpen = useMemo(() => isOpen, [isOpen]);
useEffect(() => {
register({ open, close });
}, [register]);
useWindowScrollLock(isModalOpen);
return isModalOpen ? (
<Portal>
<Container>
<InnerModal>
<Close onClick={close}>X</Close>
{children}
</InnerModal>
</Container>
<Background onClick={close} opaque={isBackgroundOpaque} />
</Portal>
) : null;
};
export default Modal;

TypeError: v.setActiveFile is not a function

I'm creating my project in ReactJS. In this projects, there is the page into I should make a textarea. This page is divided in two part, the sidebar, where you see all the files of the current workspace, and the editor, an html textarea. When I click a button, I would like that the current file changes. So, I created a context to pass to the child element. Here is the code of the Parent component, we can call it:
import '../styles/style.css';
import Editor from './Editor'
import Sidebar from './Sidebar'
import React, {useState} from 'react';
const WContext = React.createContext();
const EditPage = () => {
const {activeFile, setActiveFile} = useState('Empty');
return <>
<WContext.Provider value={{activeFile, setActiveFile}}>
<h1 className="edittitle">Edit Page</h1>
<div className="edit">
<Sidebar />
<Editor />
</div>
</WContext.Provider>
</>
}
export default EditPage;
export {WContext};
Then, there is the sidebar code:
import {useEffect, useState, useContext} from 'react';
import {WContext} from './EditPage';
import axios from 'axios';
const Sidebar = () => {
const v = useContext(WContext);
const [file, setFile] = useState([]);
useEffect(() => {
//request to server
}, [])
return <>
<div className="sidebar">
<h3>Files of workspace</h3>
<div className="sidesaparator"></div>
{
file.map(f=>{
return <button key={f._id} onClick={() => v.setActiveFile(f)} style={{'padding':"10px"}}>
{f.name + "." + f.extension}</button>
})
}
</div>
</>
}
export default Sidebar;
Finally, the editor code:
import sun from '../images/sun.png';
import moon from '../images/moon.png';
import {useState, useContext} from 'react';
import {WContext} from './EditPage';
const Editor = () => {
const v = useContext(WContext);
const [text, setText] = useState('//Your code');
const handleChanges = (e) => {
setText(e.target.value);
setLine(countLine);
}
return <>
<form className="editor">
<div className="opened"> {v.activeFile} </div>
<textarea name="code" value={text} className="editorText" onChange={handleChanges} spellCheck="false"
id = {bg}></textarea>
</form>
</>
}
export default Editor;
In this last file, the div with className "opened" should have as text the value of activeFile, but it is an empty string. On the other hand, in the previus file, when I click in one file, it should become the active one, but the error in the question title appears. I would like to know where I'm mistaking, and how to fix it. Thank you so much
All this can be fixed by switching from brakets to square parentesis the useState declaration

Testing a button with a method callback in a functional component

I am trying to test a react component using Enzyme. I am not able to test the click on the IconButton component and the function doesn't get called when i simulate a click.
This is how IconButton is defined on an external component.
var IconButton = function (props) {
return (React.createElement(IconButton$1, { color: 'default', onClick: props.onClick, disabled: props.disabled, size: props.size, onMouseDown: props.onMouseDown }, props.children));
};export{Button,IconButton};
This is how I am using it in my app.
import React, {useState, useEffect} from 'react';
import { Drawer } from '#material-ui/core';
import ExpandLessIcon from '#material-ui/icons/ExpandLess';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import { IconButton } from '#mycomponent/button';
export default function Component1 {
const classes = useStyles();
const [open, setOpen] = useState(true);
const handleClick = function (event) {
if (event) {
setOpen(!open);
}
else {
return;
}
};
return (
<Drawer>
<div className="classname1">
<IconButton onClick={(e) => handleClick(e)} className={classes.button, "iconBtn"}>
{open ? <ExpandLessIcon data-test="lessIcon" /> : <ExpandMoreIcon data-test="moreIcon" />}
</IconButton>
</div>
</Drawer>
);
}
Here is my test for simulating the click on the Icon Button. I also tried another way to check that the handleClick was getting called but it still fails.
const wrapper = shallow(<Component1 />);
it('Test the button click', () => {
expect(wrapper.containsMatchingElement(<ExpandMoreIcon />)).toBeTruthy()
const element = wrapper.find(".iconBtn")
const mockEvent = {target: {}};
element.simulate('click', mockEvent)
expect(wrapper.containsMatchingElement(<ExpandLessIcon />)).toBeTruthy()
})
Try changing this line:
const element = wrapper.find("button").at(0);
or you could find it by it's className from debug():
const element = wrapper.find(".MuiButtonBase-root MuiIconButton-root");
Notice that you'd simulate a click on an actual html button in such case.

How to implement API search onClick

My project is using React, Axios, and movieDB's API. I am trying to make it so that when I type out a movie title and either hit enter and/or click submit then the API will return the title of the movie searched for as an h1 element.
currently, the code works as soon as I refresh the browser and the only way for it to function properly is if I go into the code and replace ${query} with what I want to search for, ie joker. I have tried adding the onclick to the button : <button onclick="componentDidMount()">submit</button>. This did not do anything, however.
App.js
import React from "react"
import Movielist from './components/Movielist'
function App() {
return (
<div>
<input type="search" />
<button onclick="componentDidMount()">submit</button>
<h1 id="title">title</h1>
<Movielist />
</div>
)
}
export default App
Movielist.js
import React from 'react';
import axios from 'axios';
export default class Movielist extends React.Component {
state = {
title: ""
}
componentDidMount() {
const API_KEY = '*****************';
const query = document.getElementById('search');
axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${query}`)
.then(res => {
const title = res.data['results'][0]['title'];
this.setState({ title });
})
}
render() {
return (
<h1>{this.state.title}</h1>
)
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
ReactDOM.render(
<App />,
document.getElementById('root')
);
You have a few things wrong here:
The structure of your app is not great, eg. seperate out your API calls
You are calling a lifecycle method, these are called automatically
Don't use DOM selectors like getElementById in React
Use camelcase event listeners (onclick should be onClick)
Use callbacks with event listeners or they will fire immediatly
You included your API key on stackoverflow, big mistake!
Try this:
https://codepen.io/alexplummer/pen/YzwyJOW
import React, {useState} from "react";
const MovielistSearch = props => (
<>
<input type="search" onChange={e => props.saveSearchInput(e.target.value)} />
<button type="button" onClick={() => props.handleSearch()}>
Search
</button>
</>
);
const getMovies = props => {
return ['Title 1', 'Title 2', 'Title 3'];
// ADD REAL API HERE
// const API_KEY = '';
// const getMovies = await axios.get(`https://api.themoviedb.org/3/search/movie?api_key=${API_KEY}&query=${props.searchTerm}`);
// return getMovies.data;
}
const MovieList = props => (
<ul>
{props.foundMovies.map(thisMovie=><li>{thisMovie}</li>)}
</ul>
);
const App = () => {
const [searchInput, setSearchInput] = useState("");
const [foundMovies, setFoundMovies] = useState([]);
const movieSearch = ()=> {
if (searchInput == null) return;
const foundMovies = getMovies(searchInput);
setFoundMovies(foundMovies);
}
return (
<div>
<h1 id="title">Movie list</h1>
<MovielistSearch saveSearchInput={setSearchInput} handleSearch={movieSearch} />
<MovieList foundMovies={foundMovies} />
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
The problem might be here:
const query = document.getElementById('search');
It returns an HTML element. Try document.getElementById('search').innerText

Is it possible to target a className using a useState hook?

Is it possible to target a className in a useState hook? target.className == "test" is what I am looking at specifically. Is it possible to look for a class and if that class is active hide/show another div page element?
I may be way off with this but, looking for suggestions.
// Click tracking of className "test"
const [isSelected, setIsSelected] = useState(true)
const selectToggle = (target.className == "test") =>
setIsSelected({ ...isSelected, [test]: !isSelected[test] })
You can track element with its reference using useRef, check the console which will log the element it tracks:
import React, { useState, useRef, useEffect } from 'react';
import ReactDOM from 'react-dom';
const App = () => {
const [isSelected, setIsSelected] = useState(true);
const elementRef = useRef();
useEffect(() => {
console.log(elementRef.current);
});
const toggle = () => setIsSelected(s => !s);
return (
<>
{isSelected && <div ref={elementRef}>Im Tracked</div>}
<button onClick={toggle}>{`Click to ${
isSelected ? 'disable' : 'enable'
}`}</button>
</>
);
};
ReactDOM.render(<App />, document.getElementById('root'));

Categories

Resources