React SVG Component Not Always Updating - reactjs

I'm currently having an SVG object composed of a list of "blocks" that changes based on a Select selection. The problem that I have is that the shape of the blocks only appear to update when the number of blocks in the selected plan is different.
So, for example, if I start with PlanA and it consists of 2 blocks, selecting PlanB with 3 blocks will give me the result I expect. But if I then select PlanC that also has 3 blocks, the dimensions of these blocks doesn't change (though the accompanying text does).
An example for what I've done with the top level component:
const [planOptions, setPlanOptions] = useState([]); //would be filled with option info
const [selectedOption, setSelectedOption] = useState(null);
const [fullInfo, setFullInfo] = useState([]); //would contain the full selection of information
const [blocks, setBlocks] = useState([]);
<PlanChartArea
key={blocks}
blocks={blocks}
/>
function selectPlan(fullInfo, planVal) {
//get blocks by chosing correct plan and ignoring the initial "marker" element
let blockList = fullInfo.filter(elem => elem.plan == planVal && elem.point > 1);
//
////////
//----PERFORM TRANSFORMATIONS
////////
//
setBlocks(blockList);
}
const handleSelectChange = (option) => {
setSelectedOption(option);
var selection = option.value;
selectPlan(fullInfo, selection);
}
<Select
value={selectedOption}
placeholder="Select Plan..."
options={planOptions}
onChange={(e) => { handleSelectChange(e) }}
/>
And with the child component:
for (let i = 0; i < props.blocks.length; i++) {
blocksobj.push(<Block
key={i + "_bl"}
x1={props.blocks[i].x1pos}
x2={props.blocks[i].x2pos}
y1={props.blocks[i].y1pos}
y2={props.blocks[i].y2pos}
y0={y0}
maxYValue={maxYValue}
yLength={yLength}
/>);
blocktxt.push(<text
key={i + "_bt"}
x={asPercent(x1pos + 0.5 * (x2pos - x1pos))}
y={asPercent(y0 + 0.05 * yLength)}
fontWeight="bold"
dominantBaseline="auto"
textAnchor="middle"
>
{Math.round(props.blocks[i].value)/100}kg/min
</text>);
}
return (
<div style={{position:"parent", width:"100%", height:"100%"}}>
<svg width={asPercent(SVG_WIDTH)} height={asPercent(SVG_HEIGHT)}>
{blocksobj}
{blocktxt}
<XYAxis
xAxisLength={xLength}
yAxisLength={yLength}
ox={x0}
oy={y0}
/>
{xTickmarks}
{yTickmarks}
</svg>
</div>
);
I tried implementing the accepted solution from this question but to no effect. Perhaps this is because my state value is in the form of an Array? Any help would be much appreciated.

Related

Limit user input in a text field using Kendo UI grid and React

So I have a datagrid, with editable rows where the user can edit and add rows.
I want the user to have a limit on each different row cell field. For example, smallNumber will have 3 and description will have 15. I am using Kendo UI and React v18.2.0
<Grid
editField={editField}
onItemChange={itemChange}
data={dataField}
key="keys"
>
<Column field="smallNumber " title="Small NR" />
<Column field="stationDescription" title="DESCRIPTION" />
</Grid>
And itemChange func is like:
const itemChange = (event) => {
const newData = dataField.map((item) =>
item.dataFieldID === event.dataItem.dataFieldID
? { ...item, [event.field || ""]: event.value, changed: true }
: item
);
setDataField(newData);
};
If I add a maxLength={3} to the column for ex, it will give me a 500 server error. I want to use the onItemChange event, and check the value lenght for that field. If it is over 5, then the values in the state wont be updated, but i dont know how to do it. And also how to do it for different fields such as SmallNumber which needs less than 3 and Description that needs less than 15.
Found the solution:
Initialized the max length for each column
const itemsLength = {
stationDescription: 225,
stationNumber: 3
}
And then changed the newData funx like this
const newData = stations.map((item) =>
item.stationID === event.dataItem.stationID
? { ...item, [event.field || ""]: event.value.length <= itemsLength[event.field] ? event.value : item[event.field], changed: true }
: item
);

sorting tasks in three column

I have a task manager in home component I'm getting all task and I put tasks in column component
const { tasks} = useTypedSelector((state) => state.task);
const new_tasks = tasks.filter((task) => task.position == 'new');
const progress_tasks = tasks.filter((task) => task.position == 'progress');
const done_tasks = tasks.filter((task) => task.position == 'done');
<div className="columns">
<Column position="new" inner_tasks={new_tasks} />
<Column position="progress" inner_tasks={progress_tasks} />
<Column position="done" inner_tasks={done_tasks} />
</div>
then in column component I'm gettin in props inner_tasks
Column = ({ position, inner_tasks })
and render tasks depends of position
{inner_tasks &&
inner_tasks.map((item) =>
item && item.position == position ? (
in total I got 3 arrays, but only one component - column.
I want to add sorting and I want to if I selected sorting in first column tasks in first column will be sorted, but in 2 and 3 column not.
I writed sorting func
const hangleChange = (sortingType, position_name) => {
debugger;
inner_tasks.filter((item) => item.position == position_name);
sorted = inner_tasks.sort((a, b) =>
a.priorityNum > b.priorityNum ? 1 : -1
);
};
sortingType will be 'by date' or 'by priority'
position_name it's just name of column for example 'new tasks' or 'done'
ok, now I have sorted array with name sorted, but how can I replace and rerander my tasks in one column and don't change my other columns. I don't know i to change this part of code
{inner_tasks &&
inner_tasks.map((item) =>
item && item.position == position ? (
I think problem is here. Help me guys!

ReactJS - I implement Binary Search Function, it works only first time

ReactJS - I implement Binary Search Function, it works only first time but after I change the value in the input box, it always return -1 even it has the value in the Array.
Please see the following code:
import React, { useState } from 'react'
import { Container } from 'react-bootstrap'
const binarysearch = () => {
const [ insertValue, setInsertValue ] = useState(0)
var exarr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]
// Binary Search
const binarySearch = (arr, val) => {
let start = 0, end = arr.length - 1
while (start <= end) {
let mid = Math.floor((start + end)/2)
console.log(mid)
if (arr[mid] === val) {
return mid
}
if (val < arr[mid]) {
end = mid - 1
} else {
start = mid + 1
}
}
return -1
}
// End Binary Search
return (
<div>
<br />
<hr />
<Container>
<h1>Binary Search</h1>
<h4>Array = {JSON.stringify(exarr)}</h4>
<h4>Search <input type="number" onChange={e => setInsertValue(e.target.value)} /></h4>
<h3>Search {insertValue}, Result in location: {binarySearch(exarr,insertValue)}</h3>
</Container>
<hr />
</div>
)
}
export default binarysearch
First Time Load
After Change Input (Search 10 it should return 10 but -1)
The problem is the fact that e.target.value is always a string, even when the input type attribute is set to "number".
So, when you do arr[mid] === val it will be always false, since this is comparing a number to a string.
You can see this behaviour here.
To fix this, do onChange={e => setInsertValue(Number(e.target.value))}.
Or, alternatively, you can use the non strict equality comparison, which is not really recommended, by replacing the === operator by just ==.
Thank you very much #Mario Vernari
I update the below line to change from string to number, it works properly.
(Insert '+' to insertValue)
From
<h3>Search {insertValue}, Result in location: {binarySearch(exarr,insertValue)}</h3>
To
<h3>Search {insertValue}, Result in location: {binarySearch(exarr, +insertValue)}</h3>

Is there a react method to dynamically add items from an array to a div in a custom pattern

I am using a div that contains an image below which rows are added with products in them. The product gallery must look like a pyramid so below the image there will be three product rows each containing one, two and three products (each product being a custom rfce), respectively.
Illustration:
img
div1 => product
div2 => product product
div3 => product product product
I used a function that renders a string with all the desired attributes and tried using parse (from html-react-parse) to convert the string to html tags but this method seems slightly unprofessional in addition to which upon attempting to parse the string (which was formatted correctly), I am getting an error.
How can I rectify this, and is there a better method I can use?
string generation method:
const getProducts = () => {
var cur = 0
var gallery = ``
for(var i = 1; i <= 3; i++) {
var prods = ``
for(var j = 0; j < i; j++) {
const product = products[cur++]
// <Product /> is a custom rfce.
prods += `<Product
id = ${product.id}
pname = ${product.pname}
price = ${product.price}
rating = ${product.rating}
pic = ${product.pic}
altPic = ${product.altPic}
/>`
}
gallery += `<div className = 'product_row'>
${prods}
</div>`
}
return ({gallery})
}
method call:
return (
...
<div className = 'product_container'>
<img src = {banner} alt = 'banner' />
{parse(getProducts())}
</div>
...
)
the string is displayed just fine in the body before using parse but upon using it I get the following error after the line const product = products[cur++] in getProducts():
TypeError: product is undefined
Even if I get parse to work, I'd like to use another method as I'm sure there is a cleaner alternative.
Since you know the shape (JSX) you want, I would just hardcode the array chunks. Create a function to render an array into a row, and use the utility for each row, slicing the products state into sub-arrays to be rendered into the rows.
Array.prototype.slice
The slice() method returns a shallow copy of a portion of an array
into a new array object selected from start to end (end not included)
where start and end represent the index of items in that array. The
original array will not be modified.
const renderRow = row => (
<div className='product_row'>
{row.map(product => (
<Product
key={product.id}
id={product.id}
pname={product.pname}
price={product.price}
rating={product.rating}
pic={product.pic}
altPic={product.altPic}
/>
))}
</div>
);
return (
...
<div className = 'product_container'>
<img src={banner} alt='banner' />
{renderRow(products.slice(0,1))}
{renderRow(products.slice(1,3))}
{renderRow(products.slice(3,6))}
</div>
...
);

Word Counter React - Display Word Frequency

Hi I'm fairly new to React and currently trying to write a word counter. The idea is that once you type in the text box it will then display a list of all the words and the frequency of how often they're used (This would be displayed in the span tag where it says wordCounts). The issue I'm currently having is it only displays one word with the frequency when I want a list. Moreover I honestly feel like could be achieved in a completely different way but again I'm fairly new to React and learning as I go.
If I need to share any more info or more code, please let me know.
React Code
import { Component } from "react";
import "./App.css";
class App extends Component {
constructor(props) {
super(props);
this.state = {
firstValue: "",
numberOfCharacters: "",
withoutWhiteSpace: "",
numberOfWords: "",
linesCount: "",
wordSelectionCount: "",
};
}
firstHandle = (event) => {
var input = event.target.value;
const text = document.getElementById("textarea").value;
const linesCount = text.split("/\r|\r\n|\n/").length;
const numberOfCharacters = input === "" ? 0 : input.split("").length;
const withoutWhiteSpace =
input === "" ? 0 : input.split("").filter((char) => char !== " ").length;
const words =
input === "" ? 0 : input.split(" ").filter((word) => word.trim()).length;
const lines = input === "" ? 1 : input.split(/\n/g).length;
this.setState({
firstValue: input,
numberOfCharacters: numberOfCharacters,
withoutWhiteSpace: withoutWhiteSpace,
numberOfWords: words,
linesCount: lines,
wordSelectionCount: "",
});
};
// This function is responsible for the word counting
wordCounter = (e) => {
e.preventDefault();
var keys = [];
var counts = {};
const input = this.state.firstValue
.replace(/\W/g, " ")
.replace(/[0-9]/g, " ")
.split(" ")
.filter((word) => word.trim());
for (let i = 0; i < input.length; i++) {
var word = input[i];
if (counts[word] === undefined) {
counts[word] = 1;
keys.push(word);
} else {
counts[word] += 1;
keys.push(word);
// console.log(keys);
}
keys.sort();
for (let i = 0; i < keys.length; i++) {
var key = keys[i];
var result = key + " - " + counts[key];
console.log(result);
}
this.setState({
wordSelectionCount: result,
});
}
};
render() {
var numberOfCharacters = this.state.numberOfCharacters;
var withoutWhiteSpace = this.state.withoutWhiteSpace;
var words = this.state.numberOfWords;
var lines = this.state.linesCount;
var wordCounts = this.state.wordSelectionCount;
console.log(wordCounts);
return (
<div className="App">
<header className="App-header">
<form>
<h1>Character Counter</h1>
<p>
Characters <span>{numberOfCharacters}</span> Without White Space{" "}
<span>{withoutWhiteSpace}</span> Words <span>{words}</span> Lines{" "}
<span>{lines}</span>
</p>
<textarea
id="textarea"
type="text"
placeholder="Please type some text..."
value={this.firstValue}
onChange={this.firstHandle}
/>
<h1>Word Counting</h1>
{/* This button calls the wordCounter Method which should display all the Word listings */}
<button className="btn" onClick={this.wordCounter}>
Words Count
</button>
<p>
<span>{wordCounts}</span>
</p>
</form>
</header>
</div>
);
}
}
export default App;
Issue
The issue is you are not iterating over wordSelectionCount to render your data, therefore your latest value will be displayed.
You can iterate over wordSelectionCount to render all of the data. But, I have a suggestion for you
Suggestion
First suggestion will be, use functional components and react hooks.
Second, use the power of the object's [key-value] pair to store the word counts.
I have created a codesandbox example if you want to have a look. You can start adding words to see the word counter.
Solution for the existing code
instead of rendering <span>{wordCounts}</span> directly, you should iterate over it. Such as:
this.state.wordSelectionCount && Object.keys(this.state.wordSelectionCount).map(word => (
<span>{word} - {this.state.wordSelectionCount[word]}</span>
)}

Resources