I am building a React app using Bootstrap 5. I have a dynamic Accordion component grabbing tasks from a list:
import React, {useEffect, useState} from "react";
import 'bootstrap/dist/css/bootstrap.min.css'
import FadeIn from "react-fade-in";
import {PageLayout} from "./page-layout";
import TaskDataService from "../services/TaskService";
export const ProfilePage = () => {
const [tasks, setTasks] = useState([]);
const [currentTask, setCurrentTask] = useState(null);
const [currentIndex, setCurrentIndex] = useState(-1);
useEffect(() => {
retrieveTasks();
}, []);
const retrieveTasks = () => {
TaskDataService.getAll()
.then(response => {
setTasks(response.data);
console.log(response.data);
})
.catch(e => {
console.log(e);
});
};
const refreshList = () => {
retrieveTasks();
setCurrentTask(null);
setCurrentIndex(-1);
};
const setActiveTask = (task, index) => {
setCurrentTask(task);
setCurrentIndex(index);
};
return (
<PageLayout>
<FadeIn>
<div className="list row">
<div className="col-md-6">
<h4>Tasks List</h4>
<div className="accordion" id="accordionTask">
{tasks &&
tasks.map((task, index) => (
<div className="accordion-item">
<h2 className="accordion-header" id="a$index">
<button className="accordion-button" type="button" data-bs-toggle="collapse"
data-bs-target={"a" + {index}}
aria-expanded={(index === currentIndex ? "true" : "false")}
aria-controls={"a" + {index}}
onClick={() => setActiveTask(task, index)}
>
{task.title}
</button>
</h2>
<div id={"a" + {index}} className="accordion-collapse collapse"
aria-labelledby={"a" + {index}} data-bs-parent={"a" + {index}}>
<div className="accordion-body">
<div>
<label>
<strong>Owner:</strong>
</label>{" "}
{task.ownerName}
</div>
<div>
<label>
<strong>Description:</strong>
</label>{" "}
{task.description}
</div>
<div>
<label>
<strong>Status:</strong>
</label>{" "}
{task.completed ? "Completed" : "Pending"}
</div>
<div>
<label>
<strong>Due Date:</strong>
</label>{" "}
{task.startDate.split("T")[0]}
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</FadeIn>
</PageLayout>
);
};
But when I render this, I get an error:
Uncaught DOMException: Failed to execute 'querySelector' on 'Document': 'a[object Object]' is not a valid selector.
I know that a CSS element cannot start with a number, so I tried to get around this using the below syntax:
data-bs-target={"a" + {index}}
But this gets me the error listed above, which suggests that the index number passed into CSS is not coming in as the correct type to be properly read. Any tips on how to deal with this?
Someone had posted the answer, and now their reply has disappeared so I cannot give them credit. But the answer was to simply not use the double brackets.
Wrong: data-bs-target={"a" + {index}}
Right: data-bs-target={"a" + index}
In the top example, it was basically saying "Insert a property with the value Index" rather than just inserting the value of Index itself, which was a breach of syntax.
Related
I'am new using reactjs and looks like I am following the tutorial with old version of react. So, I have some roles with their permissions, the problem is when I want to make changes of the role permissions I need to preview them with previous checked permission. As you can see the image below I have the permissions data, but when I try to put them into checkbox using default checked there is nothing happen.
here is my code
RoleEdit.tsx
import axios from "axios";
import { SyntheticEvent, useEffect, useState } from "react";
import { Navigate, useParams } from "react-router-dom";
import Wrapper from "../../components/Wrapper";
import { Permission } from "../../models/permissions";
const RoleEdit = (props:any) => {
const [permissions, setPermissions] = useState([]);
const [selected, setSelected] = useState([] as number[]);
const [name, setName] = useState('');
const [redirect, setRedirect] = useState(false);
const {id} = useParams();
useEffect( () => {
(
async () => {
const response = await axios.get('permissions');
setPermissions(response.data);
const {data} = await axios.get(`roles/${id}`);
setName(data.name);
setSelected(data.permissions.map((p : Permission) => p.id));
}
)();
}, [id]);
const check = (id: number) => {
if(selected.some(s => s === id)){
setSelected(selected.filter(s => s !== id));
return;
}
setSelected([...selected, id]);
}
const submit = async (e: SyntheticEvent) => {
e.preventDefault();
await axios.post('roles', {
name,
permissions: selected
});
setRedirect(true);
}
if(redirect){
return <Navigate to="/roles"/>
}
return(
<Wrapper>
<form onSubmit={submit}>
<div className="mb-3 mt-3 row">
<label className="col-sm-2 col-form-label">Role Name</label>
<div className="col-sm-10">
<input className="form-control" defaultValue={name} onChange={e => setName(e.target.value)}/>
</div>
</div>
<div className="mb-3 row">
<label className="col-sm-2 col-form-label">Permissions</label>
<div className="col-sm-10">
{permissions.map((p: Permission) => {
return(
<div className="form-check form-check-inline col-3" key={p.id}>
<input className="form-check-input" type={"checkbox"}
value={p.id}
defaultChecked={selected.some( s => s === p.id)}
onChange={()=> check(p.id)}/>
<label className="form-check-label">{p.name}</label>
</div>
)
})}
</div>
</div>
<button className="w-100 btn btn-lg btn-primary" type="submit">Save</button>
</form>
</Wrapper>
);
};
export default RoleEdit;
please help me solve the problem so I can continue the tutorial. Thankyou
function sample () {
return (
<div>
<input/>
<input/>
<input/>
.
.
?
<button onClick={ ? ? ? ? }> ADD NEW INPUT <button>
</div>
)}
Let's pretend we're working on this code. Here, by clicking 'ADD NEW INPUT' button tag, I want to input tag to keep created.
I have looked for createElement() and appendChild(), but all I can do was only append just 1 HTML element to existing one.
I want to know how we can make a function or set up a logic to solve this kind of problem.
const [input, setInput] = useState([<input defaultValue={1} />]);
return (
<div>
{input.map((item) => (
<div>{item}</div>
))}
<button
className="block p-5 mx-4 rounded-lg bg-emerald-600"
onClick={() => {
setInput([...input, <input defaultValue={input.length + 1} />]);
}}
>
Append
</button>
</div>
);
You can check the below implementation
import React, { useState } from "react";
const Input = () => <input />; //input component
const Component = () => {
const [inputs, setInputs] = useState([]); //create a state to keep all generated inputs
return (
<div>
//re-render all inputs whenever we have a new input
{inputs.map((Input, index) => (
<Input key={index} />
))}
//set a new input into the input list
<button onClick={() => setInputs([...inputs, Input])}>Generate input</button>
</div>
);
};
export function App() {
return <Component />;
};
Here is the playground
This question already has answers here:
How to access a DOM element in React? What is the equilvalent of document.getElementById() in React
(9 answers)
Closed 2 years ago.
When i click the play button, it shows an error saying:
'Cannot read property 'play' of null '
Here is my code:
import React, {useState} from 'react';
import ReactDOM from 'react-dom';
const App = () => {
const player = document.getElementById('player')
const [musicIndex, setMusicIndex] = useState(0);
const musicArray = [
{
title: 'koe no katachi',
link: 'aiko- 恋をしたのはmusic video.mp3'
},
{
title: 'stay alive',
link: 'ReZero ED Ending 2 FullEmilia (Rie Takahashi) - Stay AliveENG Sub.mp3'
},
{
title: 'Tenshi ni fureta',
link: '[K-ON AMV] 天使にふれたよ.mp3'
}
]
return (
<div className="spotify-clone">
<audio id='player' className='player' src={`Songs/${musicArray[musicIndex].link}`} controls></audio>
<h3>{musicArray[musicIndex].link}</h3>
<div className='button'>
<button onClick={() => setMusicIndex(musicIndex + 1)}>Next</button>
<button onClick={() => setMusicIndex(musicIndex - 1)}>Prev</button>
<button onClick={() => player.play()}>Play</button>
<button onClick={() => player.pause()}>Pause</button>
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('music-player'));
You need to use ref
import React, {useState,useRef} from 'react';
export const App = () => {
const playerRef = useRef<HTMLAudioElement>(null);
const [musicIndex, setMusicIndex] = useState(0);
const musicArray = [
{
title: 'koe no katachi',
link: 'aiko- 恋をしたのはmusic video.mp3'
},
{
title: 'stay alive',
link: 'ReZero ED Ending 2 FullEmilia (Rie Takahashi) - Stay AliveENG Sub.mp3'
},
{
title: 'Tenshi ni fureta',
link: '[K-ON AMV] 天使にふれたよ.mp3'
}
]
return (
<div className="spotify-clone">
<audio id='player' ref={playerRef}
className='player'
src={`Songs/${musicArray[musicIndex].link}`} controls>
</audio>
<h3>{musicArray[musicIndex].link}</h3>
<div className='button'>
<button onClick={() => setMusicIndex(musicIndex + 1)}>Next</button>
<button onClick={() => setMusicIndex(musicIndex - 1)}>Prev</button>
<button onClick={() => playerRef?.current?.play()}>Play</button>
<button onClick={() => playerRef?.current?.pause()}>Pause</button>
</div>
</div>
);
}
You could try maybe initializing the player inline:
<button onClick={() => document.getElementById('player').play()}>Play</button>
I don't know exactly if this helps
You shouldn't use document.getElementById here, as at this point of time the DOM is not yet created. So instead of doing this, you should declare a new variable with useState: const [player, setPlayerValue] = useState('');
and then in useEffect method set the value of player variable with document.getElementById('player').
I've got a container div with a lot of children, and an overflow-x set to scroll.
This allows me to scroll the content to the left and right using my trackpad (or drag on mobile).
But I really would like the default scroll position to be the maximum horizontal value. (i.e. scrolled all the way to the right). The relevant code for this is Element.scrollLeft = <some large value>.
I'm trying to figure out how to achieve this with React and callback refs. I have the following code:
const containerRef = useCallback(
(node) => {
if (node !== null) {
node.scrollLeft = values.length * 20;
}
},
[values.length]
);
return (
<div className={"scroll-wrapper"} ref={containerRef}>
values.map(()=>{
// Some stuff in here that's irrelevant.
});
</div>
)
(This code was based on https://reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node)
When I put a breakpoint in the useCallback function, I see it is properly invoked with a non-0 value, but right after the call when I inspect node.scrollLeft it is still 0.
When I inspect the DOM in Chrome, I can actually force the scrollLeft of the element to be high number, which does work.
So why doesn't the React useCallback ref work?
You should use useRef hook to work with refs.
To create ref use this:
const containerRef = useRef();
And add it to ref of div as you did before:
<div className={"scroll-wrapper"} ref={containerRef}>
...
</div>
And you should use useEffect and call it one time to set scrollLeft to the component in ref:
useEffect(() => {
ref.current.scrollLeft = 4000;
}, []);
Here is the full example: https://codesandbox.io/s/lingering-glitter-s0gok?file=/src/App.js
This is how I fixed
then just make some css issue for that
const [scrollX, setScrollX] = useState({
side: ""
});
const scrollLeftRef = useRef();
const handleScroll = (data) => {
setScrollX(prev => ({ ...prev, side: data.side }));
}
useEffect(() => {
if (scrollX.side === "right") {
scrollLeftRef.current.scrollLeft += 200;
} else {
scrollLeftRef.current.scrollLeft -= 200;
}
}, [scrollX]);
<div class="slide-sample">
<div id="slideRight" onClick={() => handleScroll({ side: "left" })} class="preSlide">
<i className="fas fa-caret-square-left" />
</div>
<div ref={scrollLeftRef} class="slideouter">
<div class="slideinner srcl">
<ul>
{
categories.map((category, i) => {
return (
<Button
key={category.id}
onClick={() => { dispatch(filterfood(category.name)); handleItemClick(i) }}
variant={activeclass === i ? "contained" : "outlined"}
size="medium"
>
{category.name}
</Button>
)
})}
</ul>
</div>
</div>
<div id="slideRight" onClick={() => handleScroll({ side: "right" })} className="nextSlide">
<i className="fas fa-caret-square-right" />
</div>
</div>
i have a component
import React from 'react'
export default function FilterRow (props){
return (
<div className="d-flex flex-row justify-content-between align-items-baseline">
<label className="label-filter text-capitalize" htmlFor={props.id}>{props.text}</label>
<button id={props.id} className="btn-filter" type="button" data-toggle="collapse" data-target={props.target}
name={props.name} onClick={props.buttonHandler}>
{ props.btnValue ? "-" : "+" }
</button>
</div>
)
}
and parent component that passing props and fuction to this component
fuction:
handleButtonStates = (event , prevState) => {
const { name } = event.target
console.log("hi from" , name );
this.setState(prevState => {
return {[name] : !prevState[name]}
})
}
usage in parent component:
<FilterRow id="sort-btn" text="sort by" btnValue={this.state.sortBtn}
buttonHandler={this.handleButtonStates} target="#collapseSort" name="sortBtn"/>
when i use this function to conditional render - and + it works fine
but when i change - and + on fas icons or any other html tag like this
{ props.btnValue ? <label>y</label> : <label>no</label> }
function start to return undefined instead of button name , why is this working like that and how can i fix it?
Some typos in your code:
Labe => label and <Label => <Label>