Rendering an array in React - javascript

I'm trying to render my arr in my render function.The code doesn't give any error.Here I'm trying to calculate the fibonacci series and display it on the screen.The problem with the code is when I enter a certain number in the text field it doesn't print the series on the screen but when I clear the text field without refreshing it prints the correct series on the screen.I can't understand where this error comes from.
import React, { useState } from 'react';
import { Button, TextField } from '#material-ui/core';
import './App.css';
function App() {
const [limit, setLimit] = useState(1);
const [arr, setArr] = useState([]);
function handleGenerateFibonacci(event) {
for (const n of generator(limit)) {
arr.push(n)
setArr(arr)
}
event.preventDefault();
}
function* generator(limit) {
var i = 0;
var a = 0;
var b = 1;
yield a;
yield b;
for (i = 1; i <= limit - 1; i++) {
var fibNumber = a + b
yield fibNumber
a = b
b = fibNumber
}
}
return (
<div className="App">
<form >
<h1>Generating the Fibonacci Series</h1>
<h2>Enter the range</h2>
<TextField onChange={e => setLimit(e.target.value)}></TextField>
<br />
<Button onClick={handleGenerateFibonacci} type="submit" style={{
height: 40,
width: 160,
borderRadius: 10,
backgroundColor: "Blue",
marginLeft: 50,
marginRight: 50,
marginTop: 20,
fontSize: 16,
color: "white"
}} >Calculate</Button>
<br />
<h2>Result:</h2>
<div >
<p>Fibonacci Series : {"[ " + arr + " ]"}</p>
</div>
</form>
</div>
);
}
export default App;

Because when you click button your code doesn't change value itself, just content of value
function handleGenerateFibonacci(event) {
for (const n of generator(limit)) {
arr.push(n)
setArr(arr)
}
event.preventDefault();
}
...so system doesn't really know that your array has been changed, so better write it this way
function handleGenerateFibonacci(event) {
for (const n of generator(limit)) {
arr.push(n)
}
setArr([...arr])
event.preventDefault();
}
PS when you clean the text field, limit property of state is correctly changing, so system refresh the components

Try this approach,
function handleGenerateFibonacci(event) {
for (const n of generator(limit)) {
setArr((a) => [...a, n]);
}
event.preventDefault();
}
working demo - https://codesandbox.io/s/festive-mayer-xu91t?file=/src/App.js:270-423

Related

How to re-render the DOM quickest in react

I am Trying to move a text across the screen on a fixed amount of time and for that I have come up with the following code
import React, { Component, useState, useEffect, useRef } from "react";
import { PrismCode } from "react-prism";
import { Player, ControlBar } from "video-react";
import { Button } from "reactstrap";
import { getSub_Millis } from "../../services/srtreader";
export default function Rythmoband(props) {
const initialPosition = useRef(
props.rythmoPosition === undefined ? "30%" : props.rythmoPosition
);
const [number, setnumber] = useState(
props.dialogueNumber === undefined ? 0 : props.dialogueNumber
);
const [timerCheck, setTimerCheck] = useState(true);
const [moverNumber, setMoverNumber] = useState(0.001);
const [currentTime, setCurrentTime] = useState(0);
const textMover = () => {
let x = parseFloat(initialPosition.current);
let start = getSub_Millis(props.time[number][0]);
let end = getSub_Millis(props.time[number][1]);
let timeToMove = start - end;
setMoverNumber((timeToMove / 2500) * props.player.playbackRate);
setCurrentTime(props.time.currentTime);
x = x + moverNumber;
let y = `${x}%`;
initialPosition.current = y;
};
setTimeout(() => {
textMover();
timercheck();
backChecker();
}, 0.1);
const timercheck = () => {
if (
getSub_Millis(props.time[number][1]) - props.player.currentTime * 1000 <
1
) {
initialPosition.current = "30%";
setnumber(number + 1);
}
};
const backChecker = () => {
for (let index = 0; index < props.time.length; index++) {
if (
getSub_Millis(props.time[index][1]) > props.player.currentTime * 1000 &&
getSub_Millis(props.time[index][0]) < props.player.currentTime * 1000
) {
setnumber(index);
}
}
};
return (
<>
<div
style={{
width: window.innerWidth,
background: "#FF8232",
marginTop: "20px",
height: "75px",
position: "relative",
border: "5px solid black",
}}
>
<div
style={{
width: "5px",
background: "red",
marginTop: "20px",
height: "75px",
position: "absolute",
top: "-25%",
left: "25%",
}}
></div>
<strong
style={{
position: "absolute",
left: initialPosition.current,
top: "25%",
fontSize: "2rem",
}}
>
{props.dialogue[number]}
</strong>
</div>
</>
);
}
In the above code setTimeout is responsible for calling the same functions repeatedly after a set time which in turn moves the text across the screen in a calculated manner. Now the problem is that the text element's movement is blocky rather then smooth although I have tried altering the time intervals and the movement speeds as well. I would appreciate if some one could help me figure out a better way to make the text move smoother.
For performance, avoid updating state everytime
The setTimeout accepts milliseconds, means 0.1milliseconds is very hard for performance, maybe 0.1 seconds.
There are lot of animation packages on React, this one is very mechanic
The solution: Use css transition instead

How to change the font size of the bullets of an unordered list item in draftjs?

I have a rich text editor built using draftjs. I implemented the functions for changing the font size, font family, and color of the text in the editor. I also implemented the functionality of adding unordered and ordered list items.
The issue is whenever I add an unordered or ordered list to a block of text, the font size of the bullet is always fixed and it does not correspond to the size of the text in that block.
For example the bullet size is always 10px even if the text has a font size of 20px or 40px or 10px. So, is there any way to make the bullet size the same as the size of the text in that block.
Google slides exhibit the correct behaviour, like the font size of the bullet changes depending upon the size of the text in that block.
So can anyone help me in finding a solution for this issue where I can change the style of the bullet depending upon the style of the text in that block.
The issue is explained in the below image.
Here is some workaround working for me: completely remove the base bullets and replace them using draftjs style of the first character of each li. Those are then rendered by the blockRendererFn callback of the Editor:
First, remove the normal list styling and prepare yours:
ul, ol {
list-style: none!important;
margin-left: 0!important;
li {
.liContent {
display: flex;
div { width: 100%; }
.bullet {
align-self: center;
margin-right: .5em;
}
}
&::before, &::marker {
display: none !important; // Not working everytimes (?!), thus content = ''
content: '';
}
}
}
Then, simply adapt this code for your needs (you may add whatever bullets/levels you need in the baseBullets constant:
const orderedList = 0, unorderedList = 1;
const baseBullets = [[1, 'a'], ['•', '▫', '▪']];
const leavingLevel = 0, initiatingLevel = 1, crossingLevel = 2;
// Returns the previous or the next bullet of a given bullet
function GetSisterBullet(idx, type, whichSister) {
if (type === orderedList) {
if (Number.isInteger(idx)) return idx + whichSister;
else return String.fromCharCode(idx.charCodeAt(0) + whichSister); // Let's assume, for now, that we won't have more than 26 sublevels...
} return idx;
}
// Gets the bullet of the very first entry of a given level
function GetLevelBaseBullet(depth, type, action) {
let curLevelBaseIdx = baseBullets[type][depth % baseBullets[type].length];
return action === leavingLevel ? GetSisterBullet(curLevelBaseIdx, type, -1) : curLevelBaseIdx;
}
// Set the bullet to be displayed for the next list/sublist entry
function SetLevelNextEntryBullet(type, depth, action = crossingLevel) {
switch (action) {
case crossingLevel: listEntriesLastIdx[type][depth] = GetSisterBullet(listEntriesLastIdx[type][depth], type, 1); break;
case leavingLevel:
listEntriesLastIdx[type].splice(depth, 1);
if (depth > 0 && !listEntriesLastIdx[type][depth - 1]) // Happens when two separate lists fellow each other
listEntriesLastIdx[type][depth - 1] = GetLevelBaseBullet(depth - 1, type, initiatingLevel); //
break;
default: listEntriesLastIdx[type][depth] = GetLevelBaseBullet(depth, type, action); break;
}
}
// And finaly, the Editor blockRendererFn callback
const handleBlockRender = block => {
let blockType = -1;
switch (block.getType()) {
case 'ordered-list-item': blockType = orderedList; break;
case 'unordered-list-item': blockType = unorderedList; break;
default: break;
}
if (blockType >= 0) { // List block/tree
// Update the levels entries counters
if (block.depth == curListLvl)
SetLevelNextEntryBullet(blockType, curListLvl)
else {
if (!listEntriesLastIdx[blockType])
listEntriesLastIdx[blockType] = [];
//-----
if (block.depth < curListLvl) SetLevelNextEntryBullet(blockType, curListLvl, leavingLevel);
else SetLevelNextEntryBullet(blockType, block.depth, listEntriesLastIdx[blockType][block.depth] ? undefined : initiatingLevel);
curListLvl = block.depth;
}
// Rendering
let levelIndexToDisplay = listEntriesLastIdx[blockType][curListLvl]; // Important to pass through an other variable because of the callback it will be used in
let HTMLStyles = EditorStylesToHTMLStyles(block.getInlineStyleAt(0)); // See bellow for this function definition, if you don't have your own function already
return {
component: props =>
<div className="liContent">
<span className="bullet" style={HTMLStyles}>{`${levelIndexToDisplay}${blockType === orderedList ? '.' : ''}`}</span>
<EditorBlock {...props} />
</div>
};
}
else {
// Leaving the actual list tree
listEntriesLastIdx = [];
curListLvl = -1;
}
// Rendering all other blocks here, if needed, or return nothing for the generic rendering
...
};
And, if you don't have yours, the functions which retrieve the HTML style at a given position of draftjs content, to also adapt depending on your needs:
function EditorStylesToHTMLStyles(editorStyles) {
return editorStyles
.map(editorStyle => GetHTMLStyles(editorStyle))
.toArray()
.reduce((acc, styles) => { return { ...acc, ...styles }; }, {});
}
function GetHTMLStyles(editorStyle) {
let matches = null;
if (matches = editorStyle.match(/fontsize-(.*)/))
return { fontSize: matches[1] + 'px' };
else if (matches = editorStyle.match(/color-(.*)/))
return { color: matches[1] };
else if (matches = editorStyle.match(/fontfamily-(.*)/))
return { fontFamily: matches[1] };
else switch (editorStyle) {
case 'BOLD': return { fontWeight: 'bold' };
case 'ITALIC': return { fontStyle: 'italic' };
case 'SUPERSCRIPT': return { fontSize: '.7rem', position: 'relative', top: '-.5rem' };
case 'SUBSCRIPT': return { fontSize: '.7rem', position: 'relative', bottom: '-.5rem' };
case 'UNDERLINE': return { textDecoration: 'underline' };
case 'STRIKETHROUGH': return { textDecoration: 'line-through' };
default: return {};
}
}

Pass into a new array and show a message - which dices finishes first

I have create two dices that are 1 until 11 slots.I want to display the results.If red wins (for example I want to show me a message R wins or if B ends first the B wins).Also, I want to display the results in a new table.I am trying to do that but I can't. Javascript code is this.
// basic game settings
const gameSettings = {
length: 12,
die: {
min: 1,
max: 8,
}
}
// define "actors"
let gameItems = [{
name: "R",
bgColor: "red",
color: "white",
position: 1,
},
{
name: "B",
bgColor: "black",
color: "white",
position: 1,
},
]
// the random function
// source: https://stackoverflow.com/questions/4959975/generate-random-number-between-two-numbers-in-javascript
function randomIntFromInterval(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
// -----------------
// creating a row & cells
const rowHtml = (gameItem, cols) => {
let html = `<div class="row" style="grid-template-columns: repeat(${ cols }, 1fr)">`
for (let i = 0; i < cols; i++) {
let marker = "-"
let background = "white"
let color = "black"
if (gameItem.position - 1 === i) {
marker = gameItem.name
background = gameItem.bgColor
color = gameItem.color
}
html += `<div class="cell" style="background: ${ background }; color: ${ color }">${ marker }</div>`
}
html += `</div>`
return html
}
// updating rows
const updateRows = (container, gameItems, cols) => {
let html = ''
for (let i = 0; i < gameItems.length; i++) {
html += rowHtml(gameItems[i], cols)
}
container.innerHTML = html
}
// setting container
const container = document.getElementById('container')
// set up board for first time
updateRows(container, gameItems, gameSettings.length)
// -----------------
// action buttons
const btnRoll = document.getElementById("rollTheDice")
const btnResetBoard = document.getElementById("resetBoard")
// roll action
btnRoll.addEventListener('click', function() {
const {
length,
die: {
min
},
die: {
max
}
} = gameSettings
gameItems = gameItems.map(item => ({
...item,
position: item.position + randomIntFromInterval(min, max),
}))
updateRows(container, gameItems, length)
})
// reset action
btnResetBoard.addEventListener('click', function() {
const {
length
} = gameSettings
gameItems = gameItems.map(item => ({
...item,
position: 1,
}))
updateRows(container, gameItems, length)
})
and the code of html,css is this
.row {
display: grid;
grid-template-rows: 1fr;
}
.cell {
box-sizing: border-box;
border: 1px solid black;
display: flex;
justify-content: center;
}
<div id="container"></div>
<hr />
<button id="rollTheDice">ROLL THE DICE</button><br />
<button id="resetBoard">RESET BOARD</button>
Its really simple solution but you can work on it and extend to really have what you want.
I would done it like so apply some check of each player score on each click on roll the dice button, of course after all randomizing happens to prevent need clicking another time to get results.
let winnerScore = 0;
let winner = '';
gameItems.forEach(item => {
if (item.position >= 11) {
winner = item.name;
winnerScore = item.position;
}
});
if (winnerScore >= 11) {
console.log('[winner_result]', winner, ' wins game!');
}
Of course instead of using this console.log on detect that someone won you can easily exapnd it to display scores or really what you want on screen. I think the best way to do that is to really get all properties from "actor" object in loop check.
This solution will really decreas number of loops you need to do.
To really show results you'll have to add function or outputing it into html div element like so
if (winnerScore >= 11) {
console.log('[winner_result]', winner, ' wins game!');
document.getElementById('results').innerHTML = `${winner} wins game!`;
}
to get it working really you also need to add somewhere in you html strucure div with id "results" or anything else really but you have to remeber to swap ip too on js script
Hope It answers you're needs.
Below link to codepen which I was working on this solution:
https://codepen.io/snoh666/pen/jOMamMy

How can I prevent elements breaking to a new line because of margin?

I am trying to line all the elements in a single line. I wanted to make this dynamic so the width that I used is with percentage. What happens is that some of the elements are breaking to a new line. I understand that this is happening because of the margin and that the width does not include the margin in it's calculation. What can I do?
Here is my code:
import React, { useState, useEffect } from 'react';
const randomIntFromInterval = (min, max) => {
return Math.floor(Math.random() * (max - min + 1) + min);
}
const Dummy2 = () => {
const [arr, setArr] = useState([]);
const [length, setLength] = useState(10);
useEffect(() => {
generateArray();
}, [length]);
const generateArray = () => {
const temp = [];
for(let i = 0; i < length; i++) {
temp.push(randomIntFromInterval(7, 107));
}
setArr(temp);
}
const handleLength = (e) => {
setLength(e.target.value);
}
const maxVal = Math.max(...arr);
return (
<div>
<div className="array-container" style={{height: '50%'}}>
{arr.map((value, idx) => (
<div className="array-element"
key={idx}
style={{height: `${(value * 100 / maxVal).toFixed()}%`,
width: `${100 / length}%`,
margin: '0 1px',
display: 'inline-block',
backgroundColor: 'black'}}
></div>))
}
</div>
<div>
<button onClick={() => generateArray()}>New array</button>
</div>
<div className="slider-container">
1
<input type="range"
min="1"
max="100"
onChange={(e) => handleLength(e)}
className="slider"
id="myRange"
/>
100
</div>
{length}
</div>
);
}
export default Dummy2;
Have you tried using calc() CSS function for calculating width? It allows to combine px and percentage, so you can subtract your margin from the element's width.

Updating attribute value in Loop - Reactjs

I am trying to make a image slide from left to write and right to left infinitely in a loop in my react component.
I am not able to change the left property of the style attribute.
Here is the relevant piece of code
let lastRight = 0;
let multiplier = 1;
const images = [One, Two, Three, Four];
class ImageStream extends Component {
state = {
position: [0, 640, 1280, 1920]
};
changeLeft() {
if (lastRight > 2000) {
multiplier = -1;
} else if (lastRight < -600) {
multiplier = 1;
}
for (let i = 0; i < this.state.position.length; i++) {
this.state.position[i] = (this.state.position[i] + (multiplier * 1));
lastRight = this.state.position[i];
}
}
componentDidMount() {
this.intervalID = setInterval(this.changeLeft.bind(this), 200);
}
componentWillUnmount() {
// use intervalId from the state to clear the interval
clearInterval(this.intervalId);
}
renderImage(imageUrl, index) {
return (
<img
src={imageUrl}
style={{ left: this.state.position[index] }}
key={index + "_image"}
/>
);
}
render() {
return (
<div id="image-scroll" className="mt-4">
{images.map((imageUrl, index) => this.renderImage(imageUrl, index))}
</div>
);
}
}
export default ImageStream;
What I am expecting is that the left property of the style attribute of the image changes because the position arrays is changing regularly.
I am fairly new to React and would love to know if I am doing this wrong/right.
Thank you in advance for looking into it.
You were not using setState to change the state, also tweaked a few numbers and CSS to get the things running. Do check it out
let lastRight = 0;
let multiplier = 1;
const images = ["One", "Two", "Three", "Four"];
class ImageStream extends React.Component {
state = {
position: [0, 640, 1280, 1920]
};
changeLeft() {
if (lastRight > 2000) {
multiplier = -1;
} else if (lastRight < -600) {
multiplier = 1;
}
let posArr = [...this.state.position]
for (let i = 0; i < posArr.length; i++) {
let pos = posArr[i]
, newPos = pos + (multiplier * 10)
posArr[i] = newPos
this.setState({ position: posArr })
lastRight = newPos
}
}
componentDidMount() {
this.changeLeft = this.changeLeft.bind(this)
this.intervalID = setInterval(this.changeLeft, 10);
}
componentWillUnmount() {
// use intervalId from the state to clear the interval
clearInterval(this.intervalId);
}
renderImage(imageUrl, index) {
return (
<img
src={imageUrl}
style={{ left: this.state.position[index] }}
key={index + "_image"}
/>
);
}
render() {
return (
<div id="image-scroll" className="mt-4">
{images.map((imageUrl, index) => this.renderImage(imageUrl, index))}
</div>
);
}
}
// Render it
ReactDOM.render(
<ImageStream />,
document.getElementById("react")
);
img{ position: relative; width: 100px; height: 100px; outline: 1px solid blue;}
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

Categories

Resources