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);
}
Related
I'm trying out the new react for functions without class and i'm trying to use setstate but it's not working, saying undefined if i used it straight or saying it's not a function if i remove this. from it.
here is my code
import './App.css';
import React, { useState } from 'react';
function App() {
var [sum1, setSum1] = useState(0);
var [sum2, setSum2] = useState(0);
function handleClick(props) {
let buttonText = props.target.value;
let inputText = document.getElementById('result').value;
console.log(inputText + buttonText);
// let sum1 =setState;
// let sum2 =setState;
let total = inputText + buttonText;
if (total.charAt(0) === "0") {
} else {
document.getElementById("result").value = total;
}
if (props.target.value === "clear") {
document.getElementById("result").value = "";
}
if (props.target.value === "+") {
sum1 =document.getElementById("result").value.slice(0, -1);
setSum1({sum1:parseFloat(sum1)});
// alert(sum1);
document.getElementById("result").value = "";
alert(sum1);
}
if (props.target.value === "=") {
sum2 =document.getElementById("result").value.slice(0, -1);
setSum2({sum2:parseFloat(sum2)});
alert(sum1+sum2);
document.getElementById("result").innerText = sum1+sum2;
}
return total;
}
// function handleClick2(props) {
// document.getElementById("ans").innerHTML = eval(document.getElementById("result").value);
// }
return (
<div className="App">
<div id="ans" className="answer">Ans = 0</div>
<input type="text" onChange={handleClick} readOnly placeholder="0" className="inputid" id="result"/>
<hr/>
{/* calculator buttons below */}
{/* button + */}
<button className="opbut" value="+" onClick={handleClick}>+</button>
{/* button - */}
<button className="opbut" value="-" onClick={handleClick}>-</button>
{/* button × */}
<button className="opbut" value="*" onClick={handleClick}>×</button>
{/* button ÷ */}
<button className="opbut" value="/" onClick={handleClick}>÷</button>
{/* button 0 */}
<button className="calbut" value={0} onClick={handleClick}>0</button>
{/* button 1 */}
<button className="calbut" value={1} onClick={handleClick}>1</button>
{/* button 2 */}
<button className="calbut" value={2} onClick={handleClick}>2</button>
{/* button 3 */}
<button className="calbut" value={3} onClick={handleClick}>3</button>
{/* button 4 */}
<button className="calbut" value={4} onClick={handleClick}>4</button>
{/* button 5 */}
<button className="calbut" value={5} onClick={handleClick}>5</button>
{/* button 6 */}
<button className="calbut" value={6} onClick={handleClick}>6</button>
{/* button 7 */}
<button className="calbut" value={7} onClick={handleClick}>7</button>
{/* button 8 */}
<button className="calbut" value={8} onClick={handleClick}>8</button>
{/* button 9 */}
<button className="calbut" value={9} onClick={handleClick}>9</button>
{/* button ( */}
<button className="opbut" value="clear" onClick={handleClick}>C</button>
{/* button ) */}
<button className="opbut" value="Del" onClick={handleClick}>Del</button>
{/* button % */}
<button className="opbut" value="%" onClick={handleClick}>%</button>
{/* button y^ */}
<button className="opbut" value="^" onClick={handleClick}>y^</button>
{/* button total = */}
<button className="ansbut" value="=" onClick={handleClick}>=</button>
<hr/>
<div className="appName">A Simple React Calculator - Jayflo</div>
</div>
);
}
export default App;
above is the code....
as i'm trying to select a button, and onclick operator selected, setstate and delete textfield then accept another input and operate both input...
React has its own particular way of updating the DOM based on the state of (value of) the elements, so you can't use native element methods.
In this example we use three hooks: useState, useEffect, and useRef.
useState maintains the state of the input element, the updated calculation, and the final evaluation which is passed into the result div.
useEffect ensures that after the result has been completed we clear the calc state.
useRef is React's way of identifying an element. In this case, after each button is clicked, the input element is focused on again.
Here's a working example based on your code.
const { useRef, useEffect, useState } = React;
function Example() {
const [ input, setInput ] = useState('');
const [ calc, setCalc ] = useState('');
const [ result, setResult ] = useState('');
const inputRef = useRef();
// Update the input state when the value is changed
function handleInput(e) {
const { value } = e.target;
setInput(value);
}
// When the input is no longer focused (we've clicked
// a button) update the `calc` state
function handleBlur(e) {
const { value } = e.target;
setCalc(prev => prev + value);
}
// When we click a button, either update the `calc` state,
// or if we click "=", update the `result` state
function handleClick(e) {
const { nodeName, value } = e.target;
if (nodeName === 'BUTTON') {
setInput('');
inputRef.current.focus();
switch(value) {
case '=': {
setResult(eval(calc));
break;
}
case 'clear': {
setCalc('');
break;
}
default: {
setCalc(prev => prev + value);
break;
}
}
}
}
// When the result is updated, clear the `calc` state
useEffect(() => setCalc(''), [result]);
return (
<div class="container">
<div>
<div>{`Result: ${result}`}</div>
<div>{`Calc: ${calc}`}</div>
<input autoFocus ref={inputRef} onBlur={handleBlur} onChange={handleInput} type="text" value={input} />
</div>
<div class="buttons" onClick={handleClick}>
<button value="+">+</button>
<button value="-">-</button>
<button value="*">×</button>
<button value="/">÷</button>
<button value="0">0</button>
<button value="1">1</button>
<button value="2" >2</button>
<button value="3">3</button>
<button value="4">4</button>
<button value="5">5</button>
<button value="6">6</button>
<button value="7">7</button>
<button value="8">8</button>
<button value="9">9</button>
<button value="clear">C</button>
<button value="Del">Del</button>
<button value="%">%</button>
<button value="^">y^</button>
<button value="=">=</button>
</div>
</div>
);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
.container { display: flex; width: 200px; height: 200px; }
.buttons {
padding: 10px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 2px;
}
button {
background: rgba(0, 0, 0, 0.5);
border: 1px solid #111;
padding: 10px;
color: #EEE;
border-radius: 5px;
font-size: 12px;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Alternative method which reduces the number of states and doesn't have to use useRef because now there's no input.
const { useRef, useEffect, useState } = React;
function Example() {
const [ calc, setCalc ] = useState('');
const [ result, setResult ] = useState('');
// When we click a button, either update the `calc` state,
// or if we click "=", update the `result` state
function handleClick(e) {
const { nodeName, value } = e.target;
if (nodeName === 'BUTTON') {
switch(value) {
case '=': {
setResult(eval(calc));
break;
}
case 'clear': {
setCalc('');
break;
}
default: {
setCalc(prev => prev + value);
break;
}
}
}
}
// When the result is updated, clear the `calc` state
useEffect(() => setCalc(''), [result]);
return (
<div class="container">
<div class="results">
<div>{`Result: ${result}`}</div>
<br />
<div>{`Calc: ${calc}`}</div>
</div>
<div class="buttons" onClick={handleClick}>
<button value="+">+</button>
<button value="-">-</button>
<button value="*">×</button>
<button value="/">÷</button>
<button value="0">0</button>
<button value="1">1</button>
<button value="2" >2</button>
<button value="3">3</button>
<button value="4">4</button>
<button value="5">5</button>
<button value="6">6</button>
<button value="7">7</button>
<button value="8">8</button>
<button value="9">9</button>
<button value="clear">C</button>
<button value="Del">Del</button>
<button value="%">%</button>
<button value="^">y^</button>
<button value="=">=</button>
</div>
</div>
);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
.container { display: flex; }
.results { width: 40px; }
.buttons {
margin-left: 6em;
padding: 10px;
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 2px;
}
button {
background: rgba(0, 0, 0, 0.5);
border: 1px solid #111;
padding: 10px;
color: #EEE;
border-radius: 5px;
font-size: 12px;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
Let's say you want to keep track of a pending state
To make use of useState, first you have to import it
import { useState } from "react";
Then,
const [ isPending , setIsPending ] = useState(false);
the value you pass to the useState(initialValue) function will be the initial value of the state.
You can pass any value like array string or object to the useState() function
To set/change the value of the state you declared
setIsPending(true);
The value of your state is held by the isPending variable
UPDATED: About your Calculator
What you're showing is mixing react and HTML and Vanilla JS. It's not well seen and it's harder to maintain. So, the react approach is available in this new code sandbox:
(It's only handling the Sum, as an example purpose of how we can do this application using only react)
https://codesandbox.io/s/falling-star-hf90r
About USE STATE
You can see one example working here: https://codesandbox.io/s/intelligent-cori-5xi34?file=/src/App.js:50-759
The easiest way to do this is using hooks. Now, there are multiple hooks. In your case, you will need React.useState, it'll return 2 things: the value, the setter.
const [myValue, mySetter] = React.useState(defaultValue);
After that, everything will work almost the same, in this case, I've defined 3 states (sum1, sum2, and result) and 2 extra functions, handleResult and handleClear, which I could guess from your code.
export default function App() {
const [sum1, setSum1] = React.useState(0);
const [sum2, setSum2] = React.useState(0);
const [result, setResult] = React.useState(sum1 + sum2);
const handleCalculate = () => {
setResult(+sum1 + +sum2);
}
const handleClear = () => {
setResult(0);
setSum1(0);
setSum2(0);
}
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
Sum1: <input type="number" onChange={e => setSum1(e.target.value)} value={sum1}/> <br/>
Sum2: <input type="number" onChange={e => setSum2(e.target.value)} value={sum2}/> <br/>
<button onClick={handleCalculate}>Calculate</button>
<button onClick={handleClear}>Clear</button>
<h2>Result: {result}</h2>
<h3>Interactive: {+sum1 + +sum2}</h3>
</div>
);
}
function App() {
const [buttonText, setButtonText] = useState("InitialValue");
const [sum1, setSum1] = useState(0);
const [sum2, setSum2] = useState(0);
const handleClick = (props) =>{
setButtonText(props.target.value)
let inputText = document.getElementById('result').value;
console.log(inputText + buttonText);
let total = inputText + buttonText;
if (total.charAt(0) === "0") {
} else {
document.getElementById("result").value = total;
}
if (props.target.value === "clear") {
document.getElementById("result").value = "";
}
if (props.target.value === "+") {
setSum1(parseFloat(document.getElementById("result").value));
// alert(sum1);
document.getElementById("result").value = "";
}
if (props.target.value === "=") {
alert(sum1);
setSum2(parseFloat(document.getElementById("result").value))
document.getElementById("result").innerText = sum1+sum2;
}
return total;
}
return(
///my code
)
please try this now.
I am using reactjs for a login form and on the click event the client makes a request to the server with the details... For some reason when I click on mobile nothing happens.. I tried adding the cursor: pointer; property to the css file but nothing happens still. Any help is appreciated.
Code below:
import React, { Component } from "react";
import axios from "axios";
import "./login.styles.css";
import { setAccessToken, getAccessToken } from "../../authorization";
export class Login extends Component {
constructor() {
super();
this.state = {
email: "",
password: "",
accessToken: getAccessToken(),
};
}
componentDidMount() {
console.log(getAccessToken());
}
changeHandle = (e) => {
this.setState({ [e.target.name]: e.target.value });
console.log(this.state);
};
submitHandle = async () => {
try {
const response = await axios.post(
"https://linkhere.example/api/users/login",
this.state
);
setAccessToken(response.data.accessToken);
console.log(response.data.accessToken);
this.setState({ accessToken: response.data.accessToken });
this.props.history.push("/dashboard");
} catch (e) {
console.log(e);
}
};
render() {
return (
<body className="color">
<div className="container">
<div className="image">
<img alt="login" src={require("./Login.png")}></img>
</div>
<div className="top-text">
{this.state.accessToken ? <h1>{this.state.accessToken}</h1> : null}
<h1>Welcome Back!</h1>
<h3>Sign in to your account...</h3>
</div>
<div className="login-info">
<input
type="email"
name="email"
onChange={(e) => this.changeHandle(e)}
placeholder="Email"
/>
<br></br>
<input
name="password"
type="password"
placeholder="Password"
onChange={(e) => this.changeHandle(e)}
/>
<br></br>
<button onClick={this.submitHandle} type="submit">
Log In
</button>
</div>
</div>
</body>
);
}
}
export default Login;
button css
.login-info button {
color: #FFDEDE;
background-color: #3a1c4d;
border: none;
outline: none;
height: 40px;
border-radius: 10px;
font-size: 20px;
font-weight: 700;
margin-top: 20px;
cursor: pointer;
z-index: 999;
}
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
I have made a form component (CreateDocument) in Nuxt. Inside this component i made also an autocomplete (AutoCompleteFilters).
When I hit enter inside the autocomplete component, also the CreateDocument is listening to the enter key. But I only want that a specific input field is listing to the enter key event.
This is the CreateDocument component:
<template>
<div>
<Notification :message="notification" v-if="notification"/>
<form method="post" #submit.prevent="createDocument">
<div class="create__document-new-document">
<div class="create__document-new-document-title">
<label>Titel</label>
<input
type="text"
class="input"
name="title"
v-model="title"
required
>
</div>
<div class="create__document-new-document-textarea">
<editor
apiKey="nothing"
v-model="text"
initialValue=""
:init="{
height: 750,
width: 1400
}"
>
</editor>
</div>
<div class="create__document-new-document-extra-info">
<div class="create__document-new-document-tags">
<label>Tags</label>
<AutoCompleteFilters/>
</div>
<div class="create__document-new-document-clients">
<label>Klant</label>
<input
type="text"
class="input"
name="client"
v-model="client"
required
>
</div>
</div>
<Button buttonText="save" />
</div>
</form>
</div>
</template>
<script>
import Notification from '~/components/Notification'
import Editor from '#tinymce/tinymce-vue'
import Button from "../Button";
import { mapGetters, mapActions } from 'vuex'
import AutoCompleteFilters from "./filters/AutoCompleteFilters";
export default {
computed: {
...mapGetters({
loggedInUser: 'loggedInUser',
})
},
middleware: 'auth',
components: {
Notification,
Button,
editor: Editor,
AutoCompleteFilters
},
data() {
return {
title: '',
text: '',
tags: '',
client: '',
notification: null,
}
},
methods: {
...mapActions({
create: 'document/create'
}),
createDocument () {
const documentData = {
title: this.title,
text: this.text,
tags: this.tags,
client: this.client,
userId: this.loggedInUser.userId
};
this.create(documentData).then((response) => {
this.notification = response;
this.title = '';
this.text = '';
this.tags = '';
this.client= '';
})
}
}
}
</script>
And this is the AutoCompleteFilters component:
<template>
<div class="autocomplete">
<input
type="text"
id="my-input"
#input="onChange"
v-model="search"
#keydown.down="onArrowDown"
#keydown.up="onArrowUp"
#keydown.enter="onEnter"
/>
<ul
v-show="isOpen"
class="autocomplete-results"
>
<li
v-for="result in results"
:key="results.id"
class="autocomplete-result"
#click="setResult(result.name)"
:class="{ 'is-active': results.indexOf(result) === arrowCounter }"
>
{{ result.name }}
</li>
</ul>
</div>
</template>
<script>
import {mapActions} from 'vuex'
export default {
data() {
return {
isOpen: false,
results: false,
search: '',
arrowCounter: 0,
filter: null,
position: 0
};
},
methods: {
...mapActions({
getFilterByCharacter: 'tags/getTagsFromDb'
}),
onChange(e) {
this.isOpen = true;
this.position = e.target.selectionStart;
},
setResult(result) {
this.search = result;
this.isOpen = false;
},
getResults(){
this.getTagsByValue(this.search).then((response) => {
this.results = response;
});
},
async getTagsByValue(value){
const filters = {autocompleteCharacter : value};
return await this.getFilterByCharacter(filters);
},
onArrowDown() {
if (this.arrowCounter < this.results.length) {
this.arrowCounter = this.arrowCounter + 1;
}
},
onArrowUp() {
if (this.arrowCounter > 0) {
this.arrowCounter = this.arrowCounter - 1;
}
},
onEnter(evt) {
this.search = this.results[this.arrowCounter].name;
this.isOpen = false;
this.arrowCounter = -1;
}
},
watch: {
search: function() {
this.getResults();
}
},
};
</script>
<style>
.autocomplete {
position: relative;
}
.autocomplete-results {
padding: 0;
margin: 0;
border: 1px solid #eeeeee;
height: 120px;
overflow: auto;
width: 100%;
}
.autocomplete-result {
list-style: none;
text-align: left;
padding: 4px 2px;
cursor: pointer;
}
.autocomplete-result.is-active,
.autocomplete-result:hover {
background-color: #4AAE9B;
color: white;
}
</style>
Just as you did in your form to avoid "natural" form submit and replace it with a custom action:
#submit.prevent="createDocument"
... you have to preventDefault the "natural" event that submits the form when you press Enter while focusing the form.
To do so, just add .prevent to your events in the template:
#keydown.down.prevent="onArrowDown"
#keydown.up.prevent="onArrowUp"
#keydown.enter.prevent="onEnter"
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.