I am trying to create a dynamic grid for video components, but I can't figure out how to, because I have an element that is not generated dynamically. the first video element is created normally, the rest of the elements are generated using a map function.
I would like to generate rows for every 3 elements including the first.
This is the code I have now:
renderPeers() {
return (
<div className = "participants">
<video id="myVideo" ref={video => this.video = video} controls autoPlay playsInline muted></video>
{
Object.entries(this.state.peers).map(entry => {
const [peerId, peer] = entry
console.log('render peer', peerId, peer, entry)
return (
<video key = {peerId} ref={video => peer.video = video} controls autoPlay playsInline muted></video>
);
})
}
</div>
);
}
The peers state holds objects like this:
{
peer: peerObject, // simple-peer object
peerId: peerID // peerID = socket ID of peer
}
I could generate bootstrap rows and columns using a counter variable, but I have the first element which is not part of the array I use to generate the rest of the elements.
How could I make it so I have rows with 3 columns generated, including the first element?
I'm going to assume, that your state.peers looks like this:
[
{
peer: {...},
peerId: 1
},
{
peer: {...},
peerId: 2
}
]
Because you mention that you have several peers to be rendered.
Nevertheless I'm sure you are using a wrong use of Object.entries, since this function provides an iterator through you object giving you the keys and the values.
For you case this code should fix your problem:
renderPeers() {
return (
<div className = "participants">
<video id="myVideo" ref={video => this.video = video} controls autoPlay playsInline muted></video>
{
this.state.peers.map( item => {
const {peerId, peer}
return (
<video key={peerdId} ref={video => peer.video = video} controls autoPlay playsInline muted></video>
)
})
}
</div>
);
}
Basically your problem was in the Object.entries if you need to find some specific properties in your object is much better for you to use destructuring operator, like this:
const pepe = {
id: 1,
name: 'pepe'
}
const {id,name} = pepe
console.log(id, name), //1 pepe
You should use Object.entries when you want to inspect the structure of the object, usually when is an dynamic object which structure is unknown for you.
Related
I'm trying to record video in a React + TS component. I want to conditionally render the video player: if the user is recording, the video player source should be the live stream (MediaStream), but if the recording is finished, the video player source should change from the live stream to the newly created recordingURL (created with URL.createObjectURL(blob).
As you can see, I'm replacing the one <video/> tag with another, because I cannot just use HTMLMediaElement.srcObject to set a source different from a MediaStream.
<div className="VideoRecorder flex flex-col mx-2">
{recordingURL.current! ? (
<video
src={recordingURL.current}
playsInline
controls
className="RECORDED"
/>
) : (
<video
ref={(ref) => {
if (ref)
ref.srcObject = recordingStream.current;
}}
autoPlay
className="LIVE"
/>
)}
</div>
When the recording starts, the video player successfully displays the live MediaStream. When the recording stops the video player does change (I checked the render HTML and the video player with the className="RECORDED" comes in replacement for the one with the className="LIVE"), the recordingURL.current also changes from null to the object URL string but the problem is that, somehow, this new video player is still showing a live stream
The code below shows the relevant state and ref managing for the MediaRecorder.
const recordingURL = useRef<null | string>(null);
const stopRecording = () => {
const blob = new Blob(recordedChunks.current, {
type: mediaConstraints.audio ? 'audio/mpeg' : 'video/mp4'
});
setIsRecording(false);
recordingURL.current = URL.createObjectURL(blob);
setBlob(blob);
};
const initializeDevice = async () => {
try {
recordingStream.current = await navigator.mediaDevices.getUserMedia(
mediaConstraints
);
setPermissionGranted(true);
mediaRecorder.current = new MediaRecorder(recordingStream.current);
mediaRecorder.current!.ondataavailable = saveRecordingChunks;
mediaRecorder.current!.onstop = stopRecording;
} catch (error: any) {
// Exception handling...
}
};
And this HTML fragment shows the <video> element that replaced the recording one. As you can see, its src is set to a url, yet this component actually shows the data stream:
<video class="RECORDED" src="blob:http://localhost:3000/af437ac7-e77e-4d5b-a2f5-2645507448ed" playsinline="" controls=""></video>
What could be causing this problem?
Adding a key parameter to each <video> element solved the problem.
Hi I'm trying to display variants in two separate categories Color and Size, to do this I need to get data from the api, I can access "attributes", but I would like to be able to access 0 and 1 and map them, I have no idea how to do this.
{variants.length > 1 ? (
variants.attributes.map(({ name, values }) => (
<ProductOptions
key={`key-${name}`}
name={name}
values={values}
selectedOptions={selectedOptions}
setOptions={setOptions}
/>
))
) : (
<Fragment />
)}
Thank you so much!
As i understand the output of Array with 6 elements where each of that 6 has attributes and attributes is yet another array and you want to loop through those attributes so you need 2 loops. One to loop through parent array and seconds inside the child.
variants.map((variant) => {
variant.attributes.map((attribute) => {
console.log('attribute: ', attribute);
console.log('attribute id: ',attribute.id);
});
});
p.s. you may use forEach https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach but it has little difference in this case.
Also it seems like you working with ReactJS and building some JSX to put into rendered jsx. I would argue to form the array upfront and in final jsx just insert the rows, so your final jsx will be more readable, especially in cases of double loops or any more complex jsx.
const attributes = [];
variants.map((variant) => {
variant.attributes.map((attribute) => {
console.log('attribute: ', attribute);
console.log('attribute id: ',attribute.id);
attributes.push(<div key={attribute.id}>{attribute.id}</div>)
});
});
// later
return (
<div>
{attributes}
</div>
)
In my react app I need to return a line which will be created based on a list.
Here is the object,
searchCriteria: {
op_company: "039",
doc_type: "ALL"
}
and in my UI, i need to show it as a paragraph with bold values. So the hard coded code would be like below
<p>Download request for op_company: <b>{searchCriteria.op_company}</b>, doc_type: <b>{searchCriteria.doc_type}</b></p>
But the object(searchCriteria) will be changed based on the user request. So I tried like below.
const getSearchCriteria = (criteria) => {
let searchCriteria = []
searchCriteria.push('Download request for')
Object.keys(criteria).forEach((key) => {
if(criteria[key] !== '') {
searchCriteria.push(` ${key}: ${criteria[key]},`)
}
});
return searchCriteria;
}
return (
<p>
{getSearchCriteria(searchCriteria).map((item) => <span key = {item}>{item}</span>)}
</p>
);
here i'm getting the expected output. But I can't get the value as bold (highlighted). Is there another way to directly deal with html elements?
Basically I'm trying to move a single object within an Array of object, but when I move the same object once or twice it starts moving the other object in said array.
So I have tried making a new Array with .slice() then .shift(item) by it's index and then add it back in at the right index with .splice(newIndex, 0, item), once the array has been update I push it to the Redux store which updates my Megadraft(ie Draft.js) application.
I have also tried directly manipulating the original array, ie this.props.array (like your meant too with Redux) and using the keys inside of the objects instead of the indexes.
import React from 'react';
import { MegadraftPlugin, DraftJS, CommonBlock } from "megadraft"
export default class ImageGalleryBlock extends React.Component {
_moveImgOneBack = (e, images, index) =>{
e.preventDefault()
let newPlace = index - 1
if(newPlace == -1){
newPlace = images.length
}
const image = images.shift(index)
images.splice(newPlace, 0, image)
return this.props.container.updateData({ images: images })
}
_moveImgOneForward = (e, images, index) =>{
e.preventDefault()
let newPlace = index +1
if(newPlace > images.length){
newPlace = 0
}
const image = images.shift(index)
images.splice(newPlace, 0, image)
return this.props.container.updateData({ images: images })
}
render(){
return (
<CommonBlock {...this.props} actions={this.actions} title="Image
Gallery">
<BlockContent>
<div className='gallery-cms-block'>
{ this.props.images.map((obj, index)=> {
return(
<div key={obj.key} className="image-box">
<button title="Move image back one" className="move-button"
onClick={(e)=> this._moveImgOneBack(e,
this.props.data.images, index)}>◀ {index}</button>
<img className="image" src={`${obj.image.uri}?
id=${obj.image.id}`} />
<div>
<button key={obj.key} title="Move image forward one"
className="move-button" onClick={(e)=>
this._moveImgOneForward(e, this.props.data.images,
index)}>▶</button>
</div>
</div>
)
}) }
</div>
</BlockContent>
</CommonBlockMKII>
);
}
}
I expect the the button(ether forward or backward) to move said item and only the said item.
The results are that it will move the item once... maybe twice then get suck moving all the other items in the array.
... your using shift wrong:
array = ['foo', 'bar', 'not', 'feeling', 'welcome', 'by jack', 'ass users']
array.shift(whatEverIndex)
the output will always be the first index, i.e 'foo', and
because your index is correct and your using
array.splice(newIndex, 0, item)
properly your array is changing in a strange fashion.
Try copying the desired item then delete it with .splice(), like this:
const item = array[index] //copy item
array.splice(index, 1) //remove old item
array.splice(newIndex, 0, item) //place item
funny that none of you guys NaN, laruiss, Antoine Grandchamp, J-Alex took the time to actually do what you should on stackoverflow... you know help someone out. damn vete a cascarla, Good luck Reece hope this solved it for you.
Thanks #Whitepaw,
I've updated my code with:
_moveOneImgBack = (newArray, index) =>{
const arrayLength = newArray.length - 1
const newBackPlace = index == 0 ? arrayLength : index - 1
const image = newArray[index]
newArray.splice(index, 1)
// const image = images.shift(index)
newArray.splice(newBackPlace, 0, image)
this.props.container.updateData({ images: newArray })
}
and it now works perfectly, I got stuck on the fact it might have something to do with redux immutables. So thats for pointing out the misuse of .shift()
I have a loop of components that make a grid and I want some instances to have a background color change triggered by adding a class.
This question gave me ideas but I've yet to get them to work.
I have this basic markup in it.
<div className={`box ${(this.state.backgroundColor ? 'backgroundColor' : null)}`} key={i}>
</div>
And I have an array of the indexes representing the component instances that I want the color changed on
let indexes = [101, 178, 232, 545]
Currently I am doing it with plain JS, via document.querySelector('.box:nth-of-type(101'), but as this method bypasses React I want to change it.
Based on the other question I tried making an array of all the components and setting that to state, then looping through and using the indexes. I don't know how to "access" the component like this and get $$typeof: Symbol(react.element) etc
let compArr = []
this.arr.map((i) => {
compArr.push(
<div className={`box ${(this.state.backgroundColor ? 'backgroundColor' : null)}`} key={i}>
</div>
)
})
this.setState({
compArr: compArr
})
Then later loop over indexes:
indexes.map(index => {
this.state.compArr[index] ===> ??stuck here??
})
Expected Output: So on an array of three instances, say I want the second one only to have the class of backgroundColor:
<div className='box' </div>
<div className='box backgroundColor'</div>
<div className='box'</div>
Only in my case, I want the instances to correlate with the indexes array
How can I set the conditional class to true in those instances I have in the array? Say I have like 500+ instances total.
If I understand you correctly this is proably what you're looking for...
let indexes = [101, 178, 232, 545]
let compArr = this.arr.map((obj, i) => {
return (
<div
className={`box${( indexes.includes(i) ? " backgroundColor" : "")}`}
key={i}
>
</div>
);
})
this.setState({
compArr: compArr
})
If your goal is only to return the given div with the className backgroundColor for every element in this.arr with its index included in the indexes array then...
You only need to iterate over the array once and can perform all the necessary logic you're currently doing performing in two.
You don't need to use Array.push() to an outside collector when using any of the pure functions like map() or reduce() because they return a new version of whatever array is iterated over.
If for whatever reason you want a distinct array of <div>s with an index in indexes then you should use reduce...
let compArr = this.arr.reduce((acc, obj, i) => {
if (!indexes.includes(i)) return acc;
return [
...acc,
<div className="box backgroundColor" key={i}></div>
)]
},[])