Height size change is not causing state update React Hooks - javascript

I'm trying to have my Carousel height resize dynamically upon change. However I can't seem trigger a state change purely from a childs height change.
Listening to children was pretty good, infact I'm not fully sure why it's not working.
The problem occurs when an error message is appended to a child within the carousel. It doesn't update.
Currently the best thing I know of to do is an interval...
Is there a better way?
import React, {useState, useEffect, useRef} from 'react';
import './Carousel.scss';
// Wrapped children components must be placed inside of <div> elements
const Carousel = ({slideTo, children}) => {
const [carouselStyle, setCarouselStyle] = useState({});
const activeRef = useRef(null);
const index = slideTo - 1
const newChildren = [];
children.map((d,i) => {
let addClass = (d.props.className !== undefined) ? d.props.className: ""
const newProps = {
key: i
};
if(i === index){
addClass += " active"
newProps.ref = activeRef;
}
newProps.className = addClass.trim();
const newChild = React.cloneElement(d, newProps);
newChildren.push(newChild);
return false
});
const carouselContainerStyle = {
left: (((slideTo * 100) - 100) * -1) + "%",
width: (newChildren.length * 100)+ "%"
}
useEffect(() => {
const interval = setInterval(function(){
console.log("int");
if(activeRef != null){
if(carouselStyle.height === undefined || carouselStyle.height !== activeRef.current.clientHeight){
setCarouselStyle({
height: activeRef.current.clientHeight,
});
}
}
},50)
return () => {
clearInterval(interval)
}
},[]);
useEffect(() => {
console.log("children update");
if(activeRef.current !== null){
setCarouselStyle({
height: activeRef.current.clientHeight,
});
}
},[slideTo,children]);
return (
<div className="carousel" style={carouselStyle}>
<div style={carouselContainerStyle} className="carousel-container">
{newChildren}
</div>
</div>
);
};
export default Carousel;
Implementation
<Carousel slideTo={slide}>
<div><SignIn/></div>
<div><SignUp/></div>
</Carousel>

Related

my 2D grid does not re-render while using with useState hook. Is there any way to re-render after I change object attributes of the elements inside?

Context: I am rendering a 2-dimensional grid, where each cell is a Node object with properties: row, col, isGood. I create the grid and and initialize the initialGrid with a full grid.
Now I have onClick action that changes the property of isGood of clicked cell would change and it should render. However even if the change happens and the re-rendering does not happen.
Below is my code.
import { useState } from "react";
import "./App.css";
class Node {
constructor(x, y) {
this.row = x;
this.col = y;
this.isGood = false;
}
}
const totalRows = 5;
const totalCols = 5;
var grid = [];
const createNodes = () => {
grid = [];
for (let row = 0; row < totalRows; ++row) {
var newRow = [];
for (let col = 0; col < totalCols; ++col) {
newRow.push(new Node(row, col));
}
grid.push(newRow);
}
console.log(grid);
};
createNodes();
const App = () => {
const [initialGrid, setGrid] = useState(grid);
const handleClick = (rowIndex, colIndex) => {
initialGrid[rowIndex][colIndex].isGood = true;
setGrid(initialGrid);
};
const getNodeClass = (node) => {
const newClassName = node.isGood === true ? "node-good" : "node";
return newClassName;
};
return (
<div>
{initialGrid.map((row, rowIndex) => {
return (
<div key={rowIndex} className="rows">
{row.map((column, columnIndex) => {
return (
<div
className={getNodeClass(column)}
id={`${rowIndex}-${columnIndex}`}
key={columnIndex}
onClick={() => {
handleClick(rowIndex, columnIndex);
}}
></div>
);
})}
</div>
);
})}
</div>
);
};
export default App;
How can I re-render the grid ?
my onClick works and I can see it on console.log however the re-rendering doesnot happen even if I call setGrid
You need to construct a new array in the call to setGrid or React won't realize it needs to re-render. You can do this with the spread operator, adding the individual elements of the old array to your new array:
const handleClick = (rowIndex, colIndex) => {
initialGrid[rowIndex][colIndex].isGood = true;
setGrid([...initialGrid]);
};
We're also told React works better if we make the call to setGrid into a function as below, which also works. Personally I'm not sure if this is strictly necessary in this example and the code is arguably less clear:
const handleClick = (rowIndex, colIndex) => {
initialGrid[rowIndex][colIndex].isGood = true;
setGrid((ary) => [...ary]);
};

Received NaN for the `children` attribute in React

I am new in React and I am building a type racer app. I am at this stage where I want to calculate to WPM (Words per minute) but for some reason the calculation returns 'NaN'. I have checked, each variable has the correct value and there are no empty values at the time of calculation.
Some pictures:
And here is the code for the App.js:
import React from 'react';
import { useState, useEffect } from 'react';
import './App.css';
import getTime from './CurrentTime.js';
const App = () => {
const [typeracertext, setTyperacertext] = useState("My name is Ruslan. ");
const [userText, setUserText] = useState("");
const [wholeText, setWholeText] = useState("");
const [startTyping, setStartTyping] = useState("");
const [endTyping, setEndTyping] = useState("");
var [countWords, setCountWords] = useState(0);
const wordsPerMinute = (startTime, endTime, words) => {
return 60 / ({endTime} - {startTime}) * words
}
const onChange = (e) => {
if (wholeText === "")
{
setStartTyping(getTime.getTime);
}
if (typeracertext.substring(0, wholeText.length+1).slice(-1) === e.target.value.slice(-1))
{
setUserText(e.target.value);
e.target.style.color = 'black';
if (typeracertext.substring(0, wholeText.length+1).slice(-1) === " ")
{
e.target.value = "";
setWholeText(wholeText + " ");
setCountWords(countWords + 1);
}
else
{
setWholeText(wholeText + ((e.target.value).slice(-1)));
}
}
else
{
e.target.style.color = 'red';
}
if (wholeText === typeracertext.substring(0, typeracertext.length-2))
{
setEndTyping(getTime.getTime);
e.target.value = "";
}
};
return (
<div className="wrapper">
<div className="box c">
<span className="userText">{wholeText}</span>{typeracertext.substring(wholeText.length, typeracertext.length)}
{endTyping != "" &&
<span className="wpmUser">{wordsPerMinute(startTyping, endTyping, countWords)}</span>}
);
}
export default App;
and the code for CurrentTime.js
import React from 'react';
const getTime = () => {
const current = new Date();
return(current.getHours()*60*60 + current.getMinutes()*60 + current.getSeconds());
}
export default {getTime};
EDIT: Here are also a proof that the values were passed:
You are not calling gettime in your set state. You are only pointing towards it
setStartTyping(getTime.getTime())
and
setEndTyping(getTime.getTime())
And why starttime and endtime are wrapped in {}. They are plain numbers.
Maybe you can do directly
const wordsPerMinute = (startTime, endTime, words) => {
return 60 / (endTime - startTime) * words
}

Trying to set window height but it gives an error in react.js [duplicate]

This question already has answers here:
Rerender view on browser resize with React
(25 answers)
Closed 1 year ago.
I use useState to get the window height but it gives a runtime error saying that window is not defined. Do you know why
Here's the code:
let [winHeight,setWinHeight] = useState(window.innerHeight)
useEffect(() => {
const list = []
for (var i=0;i<datas.length;i++){
const t = datas[i].title
const res = handleResize(i + 2)
list.push(<li ref={resl[i + 1]} style={{top: "20px",left: res + "px"}}><Anchor href={datas[i].link || `/${t.replace(/ /g, "_")}`}><a onClick={() => closeNav()} onMouseOver={() => setHovering(i)}>{t}</a></Anchor></li>)
}
setWinHeight(window.innerHeight)
setLinks(list)
}, [winHeight])
Have you tried adding window.height in useEffect's dependency array?
useEffect(() => {
const list = []
for (var i=0;i<datas.length;i++){
const t = datas[i].title
const res = handleResize(i + 2)
list.push(<li ref={resl[i + 1]} style={{top: "20px",left: res + "px"}}><Anchor href={datas[i].link || `/${t.replace(/ /g, "_")}`}><a onClick={() => closeNav()} onMouseOver={() => setHovering(i)}>{t}</a></Anchor></li>)
}
setLinks(list)
}, [window.innerHeight])
Try this:
let [winHeight,setWinHeight] = useState(0)
useEffect(()=> {
setWinHeight(window.innerHeight)
},[])
And also you need to import useEffect from 'react'.
window isn't defined because the component isn't renderer at the point you are trying to access window object.
use [window.innerHeight] not [winHeight] inside dependency Array of UseEffect
Are you running on RN? Then you can try this to get the height~
import { Dimensions } from 'react-native';
const windowHeight = Dimensions.get('window').height;
Last Post
Add the resize event listener
Following this one, it would update the winHeight when you change height of window
const lastHeight = useRef(window.innerHeight);
const [winHeight, setWinHeight] = useState(lastHeight.current);
useEffect(() => {
window.addEventListener('resize', (e) => {
const curHeight = e.currentTarget?.innerHeight;
if (lastHeight.current !== curHeight) {
lastHeight.current = curHeight;
setWinHeight(curHeight);
}
});
}, []);

i want to implement react horizontal infinite scroll. not getting proper solution . have anyone built this in react or javasrcipt

I want to implement infinite horizontal scroll. but didn't found any solution. I tried some library also but those also not working .
Sounded interesting, gave it a bash
see here
For reference
import React, { useState, useEffect } from "react";
import "./styles.css";
export default function App() {
const containerRefDiv = React.useRef();
const [width, setWidth] = useState(0);
const [currentScrollLeft, setCurrentScrollLeft] = useState(0);
const updateDivWidth = e => {
const newScrollLeft = containerRefDiv.current.scrollLeft;
if (currentScrollLeft < newScrollLeft) {
//only do this if scrolling to the right
setCurrentScrollLeft(newScrollLeft);
if (width === 0) {
//if the width is zero, it has not been initialised yet. Initialise it
setWidth(containerRefDiv.current.clientWidth + 10);
} else {
//add 10, or whatever value you want here
setWidth(previous => previous + 10);
}
}
};
useEffect(() => {
console.log("new width set: ", width);
}, [width]);
const getInnerDivStyle = () => {
if (containerRefDiv.current && width !== 0) {
//return the wdith state as the new width if there is a container ref and width is not zero
return `${width}px`;
} else {
//Initialize to a litte more than 100% to enable overflow, if no div ref available
return "101%";
}
};
return (
<div
className="App"
style={{ overflowX: "scroll", width: "100%" }}
ref={containerRefDiv}
onScroll={updateDivWidth}
>
<div style={{ width: getInnerDivStyle() }}>{width}</div>
</div>
);
}

Redux doesn't behave correctly

I just learned a little of react-redux and stuck at such problems I cannot understand and fix at least 4 days long.
First of the problem stands and can be seen at inspectors console (I use Chrome).
I have event handler at <div> inside react component. It have to be called at onClick event but it triggers at each load or reload of site.
Second, stands somewhere near reducer's function. It says me in console (dev tools) that reducers received action 'TOGGLE_TILE' and returned undefined instead of object. Should notice that reducer successfully receives state, action properties and makes some operations inside but as result nothing normal returnes.
The code of my reducer, actions, main, container, presentation components and functions provide. Please answer expanded as you can, i want to understand whats wrong and not make this mistake inside code twice.
ALSO! I using redux-thunk middleware (to functional callbacks inside actions, you know).
Inside i have:
index.js - main component
const store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
actions.js
export function toggle(id){
return{
type: 'TOGGLE_TILE',
id
};
}
export function toggleTile(id){
return dispatch => {
console.log('toggling');
dispatch(toggle(id));
};
}
tiles.js - Reducer
var i = 0;
function tiles(state = tilesContainer, action){
var openedTiles = [];
switch (action.type) {
case 'TOGGLE_TILE':
if(i < 2){
console.log('i: '+i);
state.map((value) => {
var newOpen;
if(!value.opened && action.id === value.id){
newOpen = Object.assign({}, value, {
opened: !value.opened
});
openedTiles.push(newOpen);
i++;
console.log(i, value.opened, newOpen, openedTiles);
}
return newOpen, i;
});
}else if(i === 2){
var curr, prev;
openedTiles.map((value) => {
if(!prev){
prev = value;
}else{
curr = value;
console.log("Prev and curr: "+prev, curr);
if(curr.name === prev.name){
var currRes = Object.assign({}, curr, {
disappeared: !curr.disappeared
});
var prevRes = Object.assign({}, prev, {
disappeared: !prev.disappeared
});
return {currRes, prevRes};
} else {
let currRes = Object.assign({}, curr, {
opened: !curr.opened
});
let prevRes = Object.assign({}, prev, {
opened: !prev.opened
})
return currRes, prevRes;
}
}
});
}else{
return state;
}
default:
return state;
}
console.log("tiles: "+state.forEach(value => console.log(value)));
}
const reducers = combineReducers({
tiles
});
export default reducers;
AppContainer.jsx
const mapStateToProps = (state) => {
return {
tiles: state.tiles
};
};
const mapDispatchToProps = (dispatch) => {
return {
toggle: id => {
// console.log(id);
dispatch(toggleTile(id));
}
};
};
class AppContainer extends Component {
constructor(props){
super(props);
}
componentDidMount(){
}
render() {
var prop = this.props;
console.log(prop);
return (
<div>
<AppView prop={prop} />
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppContainer);
AppView.js
class AppView extends React.Component {
constructor(props){
super(props);
this.state = {
tiles: this.props.prop.tiles,
};
this.showTiles = this.showTiles.bind(this);
this.defineRatio = this.defineRatio.bind(this);
this.toggleTile = this.toggleTile.bind(this);
}
componentDidMount(){
this.defineRatio();
}
componentWillMount(){
}
defineRatio(){
var imgClass;
let tile = document.querySelectorAll('img');
tile.forEach((value) => {
var imgSrc, imgW, imgH;
function defineImage(imgSrc){
var img = new Image();
img.src = imgSrc;
img.onload = function() {
return {
src:imgSrc,
width:this.width,
height:this.height};
};
return img;
}
var x = defineImage(value.src);
x.addEventListener('load',function(){
imgSrc = x.src;
imgW = x.width;
imgH = x.height;
// console.log(value.src, imgW, imgH);
var imgClass = (imgW / imgH > 1) ? 'wide' : 'tall';
value.classList += imgClass;
});
});
}
toggleTile(id){
this.props.prop.toggle(id);
}
showTiles(){
const boxElems = this.state.tiles.map((value, index) => {
var styles = {background: 'black'};
var tileState = value.opened ? '' : styles;
var imgState = value.opened ? 'opened ' : 'closed ';
var elem = <img key={value.id} src={value.src} alt="" className={imgState} />;
var boxElem = <div style={tileState} className="tile-box " onClick={this.toggleTile(value.id)} key={index}>{elem}</div>;
return boxElem;
});
return boxElems;
}
render(){
var tiles = this.showTiles();
return (
<div className="tiles-box">
<div className="tiles">
{tiles}
</div>
</div>
);
}
}
export default AppView;
First problem can be solved by replacing
onClick={this.toggleTile(value.id)}
with onClick={(e) => this.toggleTile(value.id)} First statement is just invoking this.toggleTile(value.id) immediately and setting the return value to OnClick event.
Regarding second you are not returning any thing from your reducer , hence state is undefined.
if(i < 2){
console.log('i: '+i);
state.map((value) => {
var newOpen;
if(!value.opened && action.id === value.id){
newOpen = Object.assign({}, value, {
opened: !value.opened
});
openedTiles.push(newOpen);
i++;
console.log(i, value.opened, newOpen, openedTiles);
}
return newOpen, i;
});
}
What is this return newOpen, i it should be return newOpen, also as this return is in a map function you have to return the mapped array as well
so use return state.map((value) => {
the problem that you have is that you are actually calling the function inside your div, thus it will get triggered each time you enter the view, so replace the following code on your showTiles()
var boxElem = <div style={tileState} className="tile-box " onClick={this.toggleTile(value.id)} key={index}>{elem}</div>;
to this:
var boxElem = <div style={tileState} className="tile-box " onClick={e => this.toggleTile(value.id)} key={index}>{elem}</div>;
and actually this should fix the error for the point 2.

Categories