Focus the last element of a map html rendering - reactjs

In my react typescript project, I have a component which generates multiple div with the same id when I map through it.
<div id="projectName" ref={ref} contentEditable="true" className="font-semibold focus:outline-none resize-none">{projectTitle}</div>
<div id="projectName" ref={ref} contentEditable="true" className="font-semibold focus:outline-none resize-none">{projectTitle}</div>
<div id="projectName" ref={ref} contentEditable="true" className="font-semibold focus:outline-none resize-none">{projectTitle}</div>
My goal is to set the focus on the last element of it when I click on my button. For that, I tried to use a method I found on another stack, but I can't have access to the focus() method like they use in the post.
I don't really know how I could process this.
var size = document.querySelectorAll("[id=projectName]")
var last = size[size.length - 1]
last.focus() // error here
The error is the following one
Property 'focus' does not exist on type 'Element'.

Instead of using querySelector(), please try to use refs when it comes to react. I see you are trying to do that but didn't implement fully.
export default function App() {
let list = [1, 2, 3, 4, 5];
let elRefs = useRef([]);
useEffect(() => {
let size = elRefs.current.length;
elRefs.current[size - 1].focus();
});
return (
<div className="App">
{list.map((x) => (
<div
id="projectName"
tabIndex="1"
ref={(el) => (elRefs.current = [...elRefs.current, el])}
key={x}
contentEditable="true"
className="font-semibold focus:outline-none resize-none"
>
x
</div>
))}
</div>
);
}
Pass in a function to ref and keep appending to the .current array. And yes as mentioned you have to use tabIndex to make non focusable items focusable.
Code sandbox link

Related

How do I resolve the "Warning: Each child in a list should have a unique "key" prop."? [duplicate]

This question already has answers here:
Understanding unique keys for array children in React.js
(27 answers)
Closed last month.
After running "yarn run dev", I am met with the following error:
Warning: Each child in a list should have a unique "key" prop.
Check the top-level render call using . See https://reactjs.org/link/warning-keys for more information.
at StarIcon
at Product (webpack-internal:///./src/components/Product.js:24:20)
at Div
at ProductFeed (webpack-internal:///./src/components/ProductFeed.js:13:24)
at main
at div
at Home (webpack-internal:///./src/pages/index.js:18:17)
Here is my Product.js file:
import React from 'react'
import Image from "next/image";
import { useState } from "react";
import { StarIcon } from "#heroicons/react/solid";
import * as CurrencyFormat from 'react-currency-format';
// import Currency from "react-currency-formatter";
const MAX_RATING = 5;
const MIN_RATING = 1;
function Product({id, title, price, description, category, image}) {
const [rating] = useState(
Math.floor(Math.random() * (MAX_RATING - MIN_RATING + 1)) + MIN_RATING
);
const [hasPrime] = useState(Math.random() < 0.5)
return (
<div className='relative flex flex-col m-5 bg-white z-30 p-10'>
<p className='absolute top-2 right-2 text-xs italic text-gray-400'>{category}</p>
<Image src={image} height={200} width={200} style="contain"></Image>
<h4 className='my-3'>{title}</h4>
<div className='flex'>
{Array (rating)
.fill()
.map((_, i) => (
<StarIcon className='h-5 text-yellow-500'></StarIcon>
))}
</div>
<p className='text-xs my-2 line-clamp-2'>{description}</p>
<div className='mb-5'>
<CurrencyFormat value={price} prefix={'$'}></CurrencyFormat>
</div>
{hasPrime && (
<div className='flex items-center spaxe-x-2 -mt-5'>
<img className='w-12' src='https://links.papareact.com/fdw' alt=""></img>
<p className='text-xs text-gray-500'>FREE Next-Day Delivery</p>
</div>
)}
<button className='mt-auto button'>Add to Basket</button>
</div>
)
}
export default Product
I have attempted to use several different types of key props throughout the files and none of the formats seem to resolve this issue. Surely I am going about this wrong. Any help here would be greatly appretiated!!
This warning is addressed in the React docs.
When you run this code, you’ll be given a warning that a key should be provided for list items. A “key” is a special string attribute you need to include when creating lists of elements.
...
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
Example code from the docs:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
Try passing a unique key while iterating and rendering jsx i.e while iterating using .map
your code should look something like this
<div className='flex'>
{Array (rating)
.fill()
.map((_, i) => (
<StarIcon className='h-5 text-yellow-500' key={i}>
</StarIcon>
))}
</div>

set first box clicked as a default at the first load

Im new in reactjs and I create a list of card by mapping in reactjs but I want my first card be clicked as a default at the first load what can i do for this code.
<div className="d-flex">
{data && data.length > 0
? data.map((item, index) => {
return (
<>
<div className="box-stock" onClick={() => selectData(item)}>
<div className="top-stock skewed p-5">
<h1>{item.symbol}</h1>
<strong className="text-center">
<span> (0.25)</span>
{item.stockNum}
</strong>
<Label className="text-center">
EPS:<span className="text-white">{item.EPS}</span>
</Label>
<Label className="text-center">
P/E:<span className="text-white">{item.PE}</span>
</Label>
</div>
</div>
</>
);
})
: 'no data'}
</div>
You can use useEffect hook to run program once it renders. Here, in useEffect pass empty array as a dependency so, that it runs only once.
useEffect(() => {
selectData(data[0])
}, [])
You can add click event on first load using javascript. The button will trigger the event on first load and you will get your first card clicked. Here is the code:
document.getElementsByClassName("box-stock").click()

how to add dynamic content inside a textarea tag using map method

i am currently working on chat application with sockets , when i get different messages i use an array and then use map method to display them in simple html tags like p etc it worked perfect but inside text-area its not working i also tried to set text-area value property with map method but only i see is [object object] . also how can i automatically move the scroll bar down when messages there are more messages.
here is the code
import { Fragment, useEffect } from "react";
import { format } from "date-fns";
const Chat = ({ name, message }) => {
const date = new Date();
const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();
console.log("so bteay", message);
return (
<Fragment>
<div>
<h3 className="d-inline-block me-3"> Chat log </h3>
{name && (
<span className="me-3 d-inline-block">
<div
class="spinner-grow spinner-grow-sm text-success"
style={{ fontSize: "10px" }}
role="status"
>
<span class="visually-hidden">Loading...</span>
</div>
</span>
)}
<small className="text-muted d-block "> {name}</small>
<textarea
cols="70"
rows="8"
value={message.map((eachMsg) => {
return (
<Fragment>
{
<small className="text-muted d-inline-block">{`${hour}:${minute}:${second}`}</small>
}
<p
className="d-block shadow p-1 fw-bold rounded text-success"
style={{ fontFamily: "cursive" }}
>
{eachMsg}
</p>
</Fragment>
);
})}
></textarea>
</div>
</Fragment>
);
};
export default Chat;
You can only pass 1 child ( text in this case ) to text area. But you are trying to pass an array. If what you meant to do is to have as many as textareas as your array, this is how you should go about it:
const texts = ["Hi", "Bye","done"];
<div>
{texts.map((text) => (
<textarea>text</textarea>
))}
</div>
but if you are trying to have 1 textarea with all your texts inside it, first you need to create a long string using join method, and then render that string.
I think that you can't set html code inside textarea, unless you want to show it as a text?

Tailwind conditional transition

I'm creating a collapse/accordion component that shows the content when clicked. I want to add a little delay to that action but what I've doesn't seem to work. Here is what I have:
const Accordion = ({ children, open }) => {
const contentSpace = useRef(null);
return (
<div className="flex flex-col">
<div
ref={contentSpace}
className={clsx(
'overflow-auto overflow-y-hidden h-0 transition-height duration-700 ease-in-out',
{
'h-full': open,
}
)}
>
<div className="py-2">{children}</div>
</div>
</div>
);
};
Any ideas?
Seems like you have to use numeric values in order to make the animation work. So I had to change h-full with a numeric height.

Gatsby don't change state with react hooks

I created a component for my gatsby site.
And I wanted to write a simply toggle functionality for it:
import React, { useState } from "react"
import './css/header.css'
function clicker(state) {
console.log(state);
}
const header = () => {
const [isExpanded, toggleExpansion] = useState(false)
return (
<div>
<nav className="main-nav">
<div className={'container container-wide'}>
{/* responsive toggle */}
<div className="block lg:hidden">
<button onClick={() => clicker(!isExpanded)}
className="flex items-center px-3 py-2 border rounded text-teal-200 border-teal-400 hover:text-white hover:border-white">
<svg className="fill-current h-3 w-3" viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"><title>Menu</title>
<path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z" />
</svg>
</button>
</div>
{/* Main Menu*/}
<div className={`${isExpanded ? `block` : `hidden`} w-full lg:flex lg:items-center lg:w-auto justify-end`} >
<div className="text-sm lg:flex-grow">
<a href="#responsive-header"
className="block mt-4 lg:inline-block lg:mt-0 text-teal-lightest hover:text-white mx-6">
Docs
</a>
<a href="#responsive-header"
className="block mt-4 lg:inline-block lg:mt-0 text-teal-lightest hover:text-white mx-6">
Examples
</a>
<a href="#responsive-header"
className="block mt-4 lg:inline-block lg:mt-0 text-teal-lightest hover:text-white mx-6">
Blog
</a>
</div>
</div>
</div>
</nav>
</div>
)
}
export default header;
The problem is: the state seems to be there.
When I click the link the clicker(state) function gives back,
whatever the initial state is.
But the toggleExpansion function does simple not work or trigger.
Or ... the component does not render according to the new state ..
I dont know.
Can somebody help?
When I use a class component it works fine - can someone tell my why?
First of all you are doing the you should capitalize the name of your component as recommended in the React Docs . The second thing is you should move your event handler logic into your Header component since it belongs to that component.
Inorder to trigger the state change fix the code in your event handler as shown in code below.
From your code I also see that you might also be missing another important concept about React Hooks. Note that inside any particular render, props and state forever stay the same and that each render has its own event handlers. Your event handlers only 'see' or 'close over(see closures)' the values of state and props for its particular render. Therefore console logging the state inside your event handler will always give you the state for the particular render. The call to setToggleExpansion only calls your function component again with the updated state and its own event handlers.
Hope that helps.
const Header = () => {
const [isExpanded, setToggleExpansion] = useState(false)
// event handler
function clicker() {
console.log(isExpanded); // logs false for the initial render
setToggleExpansion(prev => !prev);
console.log(isExpanded); // logs false for the initial render
}
return (
<button onClick={clicker}
//...
</button>
)
}
First your component name should start with capital letter as React recommended Just change it to Header
Second, change your toggle function like this:
const Header = () => {
const [isExpanded, toggleExpansion] = useState(false)
return (
<button onClick={() => toggleExpansion(!isExpanded)}
//...
</button>
)
}
And to use it just use && logical operator like this:
{
isExpanded && ( <div> ... </div> )
}

Resources