Currently, in my app, when I click on the button, it generates a random image but it bunches up everything like so. I'd like the image to show up, which are all in different sizes, have the dog's name right below it, centered and then the button below that.
Code
import * as React from "react";
import { render } from "react-dom";
import axios from "axios";
import "./styles.css";
function App() {
const [images, setImage] = React.useState("");
const [text, setText] = React.useState("");
function btnClick() {
axios
.all([axios.get("https://dog.ceo/api/breeds/image/random"),
axios.get("https://dog.ceo/api/breeds/list/all")
])
.then(axios.spread((response) => {
setImage(response.data.message);
setText(response.data.message.split('/')[4]);
}))
.catch(err => {
console.log("Error happened during fetching!", err);
});
}
return (
<div className = "App">
<img className = "Img" src={images} alt="broken"/>
<button className = "Button" onClick = {btnClick}>Doggie!</button>
<strong>{text}</strong>
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
CSS
.App {
font-family: sans-serif;
text-align: center;
}
.Button {
display: flex;
}
.Img {
max-width:100%;
height:auto;
max-height:100%;
}
Re-arranging the element should solve the issue, wrap the name with <p></p> to display it on a new paragraph.
import React from "react";
import axios from "axios";
import "./styles.css";
export default function App() {
const [images, setImage] = React.useState("");
const [text, setText] = React.useState("");
function btnClick() {
axios
.all([
axios.get("https://dog.ceo/api/breeds/image/random"),
axios.get("https://dog.ceo/api/breeds/list/all")
])
.then(
axios.spread(response => {
setImage(response.data.message);
setText(response.data.message.split("/")[4]);
})
)
.catch(err => {
console.log("Error happened during fetching!", err);
});
}
return (
<div className="App">
<img src={images} alt="broken" />
<p><strong>{text}</strong></p>
<button className="button" onClick={btnClick}>
Doggie!
</button>
</div>
);
}
You should rearrange your rendered elements, so it goes <img/> <strong/> <button />.
Then in your CSS, make sure the elements that are inline natively become block.
See snippet below.
Note: left out React as it's not related to your issue so ignore class usage.
.App {
font-family: sans-serif;
text-align: center;
}
.App strong {
display: block;
}
.Button {
width: 100%;
text-align: center;
}
.Img {
max-width:100%;
height:auto;
max-height:100%;
display: block;
}
<div class="App">
<img class="Img" src="https://i.stack.imgur.com/0WaeI.png" alt="broken"/>
<strong>{text}</strong>
<button class="Button" onClick={btnClick}>Doggie!</button>
</div>
Flexbox
If you would prefer to use flexbox styling see this thorough post about it.
Related
In React, I have a component called DivHelper which generates 2 divs one below the other - instead i want to place them side by side and I cant see the code of Divhelper generates the divs. Is there a way to access dynamically generated divs ?
For example -
///Some random code
< DivHelper/>
///Some more code
This becomes
///Some random code
<div>1 Div</div>
<div>2 Div</div>
///Some more code
and thus, the output is
1 Div
2 Div
Instead I want it to be placed on side by side reversed (like float)
2 Div 1 Div
Is this possible ?
You can dynamically change the className of your components in React.
With your CSS you can then style the component and the layout as you want.
For example:
import { useState } from "react";
import "./styles.css";
export default function App() {
const [display, setDisplay] = useState("row");
const handleClick = () => {
display === "row" ? setDisplay("column") : setDisplay("row");
};
return (
<div className="App">
<h1>Dynamic display</h1>
<button onClick={() => handleClick()}>Change display</button>
<div className={"container " + display}>
<div className="square red">First div</div>
<div className="square green">Second div</div>
</div>
</div>
);
}
And your CSS file:
.container {
display: flex;
}
.row {
flex-direction: row;
}
.column {
flex-direction: column;
}
.square {
background: "red";
height: 100px;
width: 100px;
}
.red {
background: #f00;
}
.green {
background: #0f0;
}
I'm new to Next and have been trying to make a page(index.js) that fetches data(countries) and then displays that data, where each returned element(country) has a button to go to a page(info.js) where that specific countries data will be displayed, was wondering if its possible to pass the props(all country data) to the info.js page? I've tried reading the documentation and watching YT videos but can't seem understand what i'm reading/watching.
index.js:
import Link from 'next/link'
Welcome.getInitialProps = async function (props) {
const res = await fetch('https://restcountries.eu/rest/v2/all')
const data = await res.json()
return {
data: data
}
}
const MyLink = props => {
return (
<p>
<Link href={`/info?name=${props.name}`} >
<a>Learn More</a>
</Link>
</p>
)
}
function Welcome(props) {
return (
<div>
<div className="main-content">
<style jsx>{`
.main-content {
width: 80%;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-gap: 5px;
}
.item {
border: 1px solid black;
text-align: center;
}
.item ul{
padding: 0;
}
.item ul li {
list-style-type: none;
}
`}</style>
{props.data.map(country => (
<div key={country.numericCode} className="item">
<h4>{country.name}</h4>
<p>Region: {country.region}</p>
<p>Population: {country.population}</p>
<MyLink name={country.name} borders={country.borders} currencies={country.currencies}/>
</div>
))}
</div>
</div>
)
}
export default Welcome
info.js:
import { withRouter } from 'next/router'
import Link from 'next/link'
const Info = (props) => {
return (
<div>
<h1>{props.router.query.name}</h1>
<Link href="/">
<a>Home</a>
</Link>
</div>
)
}
export default withRouter(Info)
In MyLink component instead of using Link you can create a normal div (style it like a link) and onClick of that div push it to different page using nextjs router:
//import useRouter
import { useRouter } from 'next/router'
//then call it
const router = useRouter()
const MyLink = props => {
return (
<p onClick={() => {
router.push({
pathname: `/info?name=${props.name}`,
query: { data: //data to pass },
})
}}>
<a>Learn More</a>
</p>
)
}
You can access that data in the location object in the query key
import {useLocation} from ""
const location = useLocation()
const data = location.query
So I have been looking all over the place over the past few days and cannot find anything that works. I am using React with Bootstrap. I want a stateless functional component that takes an image path and returns an img element which, when hovered over by the mouse, zooms into the image while keeping the dimensions of the element the same.
I have tried:
Changing the style attribute in the onMouseOver and onMouseOut events like so
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<img
src={imagePath}
alt=""
style={{ overflow: "hidden" }}
onMouseOver={(e) => (e.currentTarget.style = { transform: "scale(1.25)", overflow: "hidden" })}
onMouseOut={(e) => (e.currentTarget.style = { transform: "scale(1)", overflow: "hidden" })}
/>
);
};
export default ImageHoverZoom;
Creating a custom css class and applying that to the img element.
index.css:
.hover-zoom {
overflow: hidden;
}
.hover-zoom img {
transition: all 0.3s ease 0s;
width: 100%;
}
.hover-zoom img:hover {
transform: scale(1.25);
}
imageHoverZoom.jsx:
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<img
src={imagePath}
alt=""
className="hover-zoom"
/>
);
};
export default ImageHoverZoom;
I have also tried a class component with state
import React, { Component } from "react";
class ImageHoverZoom extends Component {
state = {
path: this.props.imagePath,
mouseOver: false,
};
render() {
const { path, mouseOver } = this.state;
return (
<img
className={`img-fluid w-100`}
src={path}
onMouseOver={() => this.setState({ mouseOver: true })}
onMouseOut={() => this.setState({ mouseOver: false })}
style={
mouseOver
? { transform: "scale(1.25)", overflow: "hidden"}
: { transform: "scale(1)", overflow: "hidden"}
}
alt=""
/>
);
}
}
I would ideally not like to use state as I know it gets updated asynchronously and I feel like that would lead to some lag on the client side when mousing over the image. Any help is much obliged, thank you in advance!
EDIT:
I tried Rahul's answer below in my project, as well as in a brand new project. Here are the relevant (I think) files. Same thing as before. No change on mouse over.
App.js
import "./App.css";
import ImageHoverZoom from "./common/imageHoverZoom";
function App() {
return <ImageHoverZoom imagePath="http://picsum.photos/400/600" />;
}
export default App;
imageHoverZoom.jsx
import React from "react";
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img src={imagePath} alt="" className="hover-zoom" />
</div>
);
};
export default ImageHoverZoom;
index.css
.img-wrapper {
overflow: hidden;
}
.hover-zoom img:hover {
transform: scale(1.25);
}
Wrap the img tag in a div and then hide the overflow from div:
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img
src={imagePath}
alt=""
className="hover-zoom"
/>
</div>
);
};
export default ImageHoverZoom;
add the styling to img-wrapper:
.img-wrapper{
overflow:hidden;
}
img.hover-zoom:hover {
transform: scale(1.25);
}
So I solved it with Rahul's help (Thank you!). The css notation was flipped, like Rahul suggested, and to prevent the width from changing I had to set width: 100% in img.hover-zoom
Here is the component:
const ImageHoverZoom = ({ imagePath }) => {
return (
<div className="img-wrapper">
<img src={imagePath} alt="" className="hover-zoom" />
</div>
);
};
export default ImageHoverZoom;
index.css:
.img-wrapper {
overflow: hidden;
}
img.hover-zoom {
transition: all 0.3s ease 0s;
width: 100%;
}
img.hover-zoom:hover {
transform: scale(1.25);
}
I have a question with styled components, I would like to know how to position elements from their parents, I saw that there are the following options but I do not like any.
Through props, I don't like this method because I consider that the maintainability of this is horrible since in complex cases we will have many props.
Through className, generally in styled components we don't have class since we create styled.div for example, I like to have consistency in my structure and I don't want to have class names in some and not in others.
In this case CurrentFinderLocationButton is a react component, how would you position them? Is there a way to select it and apply styles from StyledHome without className or props?
import React from "react";
import styled from "styled-components";
import CurrentLocationFinderButton from "../buttons/CurrentLocationFinderButton";
import fullLogotype from "../../assets/images/full-logotype.svg";
const Home = () => {
return (
<StyledHome>
<StyledLogotype src={fullLogotype} />
<CurrentLocationFinderButton />
</StyledHome>
);
};
const StyledHome = styled.div`
`;
const StyledLogotype = styled.img`
width: 150px;
display: block;
margin: auto;
`;
export default Home;
you can just add some styles to wrapper
const StyledCurrentLocationFinderButton = styled(CurrentLocationFinderButton)`
{any styles}
`
Finally i solved this problem binding the component class and styled components class through the props.
import React from "react";
import styled from "styled-components";
import fullLogotype from "../../assets/images/full-logotype.svg";
import CurrentLocationFinderButton from "../buttons/CurrentLocationFinderButton";
import AddressFinder from "../finders/AddressFinder";
const Logotype = ({ className }) => {
return <img className={className} alt="" src={fullLogotype} />;
};
const EntryText = ({ className }) => {
return (
<p className={className}>
Atisbalo es una app donde podrás encontrar información sobre tus locales
favoritos y enterarte de todas las promociones que oferta tu cuidad al
instante
</p>
);
};
const Home = ({ className }) => {
return (
<StyledHome className={className}>
<StyledLogotype />
<StyleEntryText />
<StyledCurrentLocationFinderButton />
<StyledAddressFinder/>
</StyledHome>
);
};
const StyledHome = styled.div``;
const StyledLogotype = styled(Logotype)`
width: 150px;
display: block;
margin: auto auto 30px auto;
`;
const StyleEntryText = styled(EntryText)`
display: block;
width: 90%;
text-align: center;
margin: auto auto 30px auto;
`;
const StyledCurrentLocationFinderButton = styled(CurrentLocationFinderButton)`
display: block;
margin: auto auto 30px auto;
`;
const StyledAddressFinder = styled(AddressFinder)`
width: 80%;
margin: auto;
`
export default Home;
I've three components with the following tree:
<Update>
<ExpenseItem>
<ExpenseItemModal>
Update takes an array of expenses and render a ExpenseItem component for each expense.
I'm using an hook to handle modal visibility. As you can expect, i'm using this modal to edit the expense attributes.
A toggle method is imported from useModal hook on ExpenseItem to open and close the modal. What I expect is to click outside of the modal and close it. But if I've another ExpenseItem with the modal set to true, it will close the current, but it will still show the other one. I want to click outside of the modal (maybe on Update component) and close all modals at once, to avoid multiple modals opened. Actually I want only on modal open at once.
These are the following components:
Upload
import { useState, useEffect } from 'react';
import useModal from '../hooks/useModal';
import ExpenseItem from './expenseItem';
import axios from 'axios';
function Update({ data }) {
useEffect(() => console.log('update component', expenses));
const saveToDatabase = () => {
axios.post('http://localhost:3001/expenses', expenses).then((res) => {
console.log('data is saved to database');
});
};
const { setIsShowing } = useModal();
const closeModals = () => setIsShowing(false);
const [ expenses, setExpenses ] = useState(data);
return (
<div>
{expenses.map((expense, index) => {
return <ExpenseItem key={index} index={index} expenses={expenses} setExpenses={setExpenses} />;
})}
<button onClick={() => saveToDatabase()}>Save</button>
</div>
);
}
export default Update;
ExpenseItem
import useModal from '../hooks/useModal';
import EditExpenseModal from './editExpenseModal';
function ExpenseItem(props) {
const { isShowing, toggle, setIsShowing } = useModal();
let { description, date, credit, debit } = props.expenses[props.index];
const updateValue = (expense, setExpenses, success) => {
const expenses = [ ...props.expenses ];
expenses.splice(props.index, 1, {
...expense
});
setExpenses(expenses);
success();
};
return (
<div>
<div className="expense-box" onClick={toggle}>
<p>{date}</p>
<div className="expense-info">
<p className="expense-info--description">{description}</p>
<p className="expense-info--debit">{debit}</p>
<p className="expense-info--credit">{credit}</p>
</div>
</div>
<EditExpenseModal
isShowing={isShowing}
hide={toggle}
expense={props.expenses[props.index]}
updateExpense={updateValue}
setExpenses={props.setExpenses}
/>
<style jsx>{`
.expense-box {
width: 800px;
border: 1px solid black;
border-radius: 2px;
margin: 25px auto;
padding: 0 10px;
}
.expense-info {
display: flex;
}
.expense-info--description {
margin: 0 auto 0 0;
}
.expense-info--debit {
color: red;
}
.expense-info--credit {
color: green;
}
`}</style>
</div>
);
}
export default ExpenseItem;
EditExpenseModal
import { useState, useEffect, Fragment } from 'react';
import { createPortal } from 'react-dom';
const EditExpenseModal = ({ expense, isShowing, hide, updateExpense, setExpenses }) => {
const { description, date, credit, debit } = expense;
useEffect(() => {
document.body.style.overflow = 'hidden';
return () => (document.body.style.overflow = 'unset');
}, []);
const [ expenseItem, setExpenseItem ] = useState({
date,
description,
category: null,
subcategory: null,
credit,
debit
});
const handleInputChange = (e) => {
const { name, value } = e.target;
setExpenseItem({ ...expenseItem, [name]: value });
};
return isShowing
? createPortal(
<Fragment>
<div>
<div className="form">
<form>
<ul>
<li className="form-inputs">
<label>Date</label>
<input type="text" name="date" defaultValue={date} onChange={handleInputChange} />
</li>
<li className="form-inputs">
<label>Description</label>
<input
type="text"
name="description"
defaultValue={description}
onChange={handleInputChange}
/>
</li>
<li className="form-inputs">
<label>Category</label>
<input type="text" name="category" onChange={handleInputChange} />
</li>
<li className="form-inputs">
<label>Subcategory</label>
<input type="text" name="subcategory" onChange={handleInputChange} />
</li>
<li className="form-inputs">
<label>Credit</label>
<input
type="text"
name="credit"
defaultValue={credit}
onChange={handleInputChange}
/>
</li>
<li className="form-inputs">
<label>Debit</label>
<input
type="text"
name="debit"
defaultValue={debit}
onChange={handleInputChange}
/>
</li>
</ul>
</form>
<button onClick={() => updateExpense(expenseItem, setExpenses, hide)}>save</button>
<button onClick={hide}>close</button>
</div>
<style jsx>{`
.form {
background: grey;
display: flex;
flex-direction: column;
position: absolute;
height: 100vh;
top: 0;
right: 0;
width: 40%;
}
.form-inputs {
display: flex;
flex-direction: column;
list-style-type: none;
padding: 1rem 2rem;
}
`}</style>
</div>
</Fragment>,
document.body
)
: null;
};
export default EditExpenseModal;
useModal Hook
import { useState } from 'react';
const useModal = () => {
const [ isShowing, setIsShowing ] = useState(false);
function toggle() {
setIsShowing(!isShowing);
}
return {
isShowing,
setIsShowing,
toggle
};
};
export default useModal;
I don't mind to change these modal structure to make it work.
In this case, to avoid these scenarios you can write a separate method to close modal,
inside ExpenseItem.js
<EditExpenseModal
isShowing={isShowing}
hide={hideModal} //instead of toggle
...
>
and write hideModal method to close modal by passing directly 'false' value instead of using! operator.
like this in useModal Hook :
function hideModal() {
setIsShowing(false);
}