Parent component doesn't change from child component - reactjs

I am trying to make a Sudoku solver. I have a parent component Board and a child component Possible which shows available options for any box in the board. I passed the state of board,selected(selected box position in the board) and function to update board as props. But when I try to change board from Possible it doesn't change unless the selected box selected is changed from parent component. I am trying to change board element from child component.
Here is my Board component.
import React, { useState } from 'react';
import Possibles from './avails.jsx';
import solve, { initBoard } from '../help';
function Board() {
const [msg, setmsg] = useState('');
const [solved, setSolved] = useState(false);
const [grid, setGrid] = useState(initBoard());
const [selected, setSelected] = useState([0, 0]);
const solveBoard = () => {
const solution = solve(grid);
setGrid(solution);
setSolved(true);
let a = true;
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
// console.log(i,j)
if (grid[i][j] === 0) {
// console.log(grid[i][j]);
a = false;
}
}
}
if (!a) {
setmsg('Invalid Board!');
} else setmsg('Here is your solution!');
};
const updatePosition = (row, col) => {
setSelected([row, col]);
};
const resetBoard = () => {
setGrid(initBoard());
setSolved(!solved);
setmsg('');
};
return (
<div className="board">
Sudoku Solver
{grid.map((row, index) => (
<div key={index} className="row">
{row.map((el, i) => (
<button
type="button"
key={i}
className={selected[0] === index && selected[1] === i ? 'el selected' : 'el'}
onClick={() => { updatePosition(index, i); }}
>
{el === 0 ? '' : el}
</button>
))}
</div>
))}
{setSolved
? <Possibles board={grid} pos={selected} setGrid={setGrid} setPos = {setSelected}/> : ''}
<button type="button" className="btn" onClick={solveBoard}>Solve</button>
<button type="button" className="btn" onClick={resetBoard}>Reset</button>
<div>{msg}</div>
</div>
);
}
export default Board;
And here is my child component Possibles.
import React, { useState, useEffect } from 'react';
import { getAvailableNumbers } from '../help';
function Possibles({ board, pos, setGrid }) {
const [possibles, setPosibles] = useState([]);
useEffect(() => {
const avails = getAvailableNumbers(board, pos);
avails.unshift(0);
setPosibles(avails, board, possibles);
}, [pos,board]);
const updateGrid = (opt) => {
// setPosibles((prev) => prev.filter((x) => x !== opt || x === 0));
board[pos[0]][pos[1]] = opt;
setGrid(board);
};
return (
<div>
{possibles.map((opt) => (
<button type="button" key={opt} onClick={() => { updateGrid(opt); }}>{opt === 0 ? 'X' : opt}</button>
))}
</div>
);
}
export default Possibles;

please change your 'updateGrid' function to -
const updateGrid = (opt) => {
// setPosibles((prev) => prev.filter((x) => x !== opt || x === 0));
board[pos[0]][pos[1]] = opt;
board = [...board]; // this will make parent component rerender
setGrid(board);
};
input to setPosibles should be an array according to first line of 'Possibles' component -
setPosibles(avails, board, possibles);
Edit - react basically uses shallow comparison for props, state to detect new available change. By using a spread operator we are a creating new array with a new memory location, this let react know about new change and rerendering is triggered.

Related

How to swipe card programatticaly in react using react-tinder card

I am fetching data from the Backend and loading them in the card using react-tinder-card
Swiping works properly but unable to swipe using the buttons
I follow the documentation but still did not work
Here is the sample
Swiping gestures are working fine.
But when implement by checking documentation things did not work
Things are not working and tomorrow is my project day
import React, { useEffect, useState, useContext, useRef } from "react";
function Wink() {
const [people, setPeople] = useState([]);
const [loading, setLoading] = useState(false);
const [currentIndex, setCurrentIndex] = useState(people.length - 1)
const [lastDirection, setLastDirection] = useState()
// used for outOfFrame closure
const currentIndexRef = useRef(currentIndex)
const childRefs = useMemo(
() =>
Array(people.length)
.fill(0)
.map((i) => React.createRef()),
[]
)
const updateCurrentIndex = (val) => {
setCurrentIndex(val)
currentIndexRef.current = val
}
const canGoBack = currentIndex < people.length - 1
const canSwipe = currentIndex >= 0
// set last direction and decrease current index
const swiped = (direction, nameToDelete, index) => {
setLastDirection(direction)
updateCurrentIndex(index - 1)
}
const outOfFrame = (name, idx) => {
console.log(`${name} (${idx}) left the screen!`, currentIndexRef.current)
// handle the case in which go back is pressed before card goes outOfFrame
currentIndexRef.current >= idx && childRefs[idx].current.restoreCard()
// TODO: when quickly swipe and restore multiple times the same card,
// it happens multiple outOfFrame events are queued and the card disappear
// during latest swipes. Only the last outOfFrame event should be considered valid
}
const swipe = async (dir) => {
if (canSwipe && currentIndex < db.length) {
await childRefs[currentIndex].current.swipe(dir) // Swipe the card!
}
}
// increase current index and show card
const goBack = async () => {
if (!canGoBack) return
const newIndex = currentIndex + 1
updateCurrentIndex(newIndex)
await childRefs[newIndex].current.restoreCard()
}
useEffect(() => {
setLoading(true);
axios
.post("http://localhost:4000/api/all-profile", { email })
.then(function (response) {
setPeople(response.data);
setCurrentIndex(response.data.length);
}
}, []);
return (
<div className="DateMainDiv">
<Header />
<div className="ProfieCards">
{people.map((person) => (
<TinderCard
className="swipe"
key={person.email}
ref={childRefs[index]}
preventSwipe={swipe}
onSwipe={(dir) => swiped(dir, person.name, person.email)}
onCardLeftScreen={onCardLeftScreen}
onCardUpScreen={onCardUpScreen}
>
<div
style={{ backgroundImage: `url(${person.image})` }}
className="Winkcard"
>
<img
onLoad={handleLoad}
src={person.image}
alt="Image"
className="TinderImage"
/>
<h3>
{person.name}{" "}
<IconButton
style={{ color: "#fbab7e" }}
onClick={() => handleOpen(person.email)}
>
<PersonPinSharpIcon fontSize="large" />
{parseInt(person.dist/1000)+"KM Away"}
</IconButton>
</h3>
</div>
</TinderCard>
))}
<SwipeButtonsLeft onClick={()=>{swipe("left")}} />
<SwipeButtonsLeft onClick={()=>{goback()}} />
<SwipeButtonsLeft onClick={()=>{swipe("right")}} />
</div>
</div>
);
}
export default Wink;

Import function from another page

I need to call function resetToken() from another page when i click on button.
resetToken() should change useState to generate new code. I don't know how to import this function to another page and use it.
I have import
import Captcha from '../../../components/Captcha/Captcha'; and displayed with <Captcha/> in return( ... )
So when i click on button I need to call function resetToken() to generate new code or call again import because I have in <Captcha/>
React.useEffect(() => {
resetToken();
},[]);
This code is Captcha.jsx
import React from 'react';
import './Captcha.css';
function Captcha({statusOfCaptcha}){
const [status, setStatus] = React.useState(undefined);
const [code, setCode] = React.useState(undefined);
const [text, setText] = React.useState("");
const [seconds, setSeconds] = React.useState(120);
function resetToken(){
//generate code
var codeGenerated = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789";
for (var i = 0; i < 6; i++){
codeGenerated += possible.charAt(Math.floor(Math.random() * possible.length));
}
setCode(codeGenerated);
//reset every 120 second
setInterval(function(){
var codeGenerated = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz123456789";
for (var i = 0; i < 6; i++){
codeGenerated += possible.charAt(Math.floor(Math.random() * possible.length));
}
setCode(codeGenerated);
setSeconds(120);
setStatus(undefined);
setText("");
}, 120000);
const interval = setInterval(() => {
setSeconds(seconds => seconds - 1);
}, 1000);
return () => clearInterval(interval);
}
React.useEffect(() => {
resetToken();
},[]);
function checkCaptcha(e){
if(e === code){
setStatus(true);
statusOfCaptcha(true);
} else{
setStatus(false);
statusOfCaptcha(false);
}
}
return (
<div className='captcha'>
<div className="background">
<p onCopy={(e) => e.preventDefault()} className="unselectable">{code}</p>
<a>{seconds}</a>
</div>
<div className='input-captcha'>
<input type="text" placeholder="Zadejte kód" value={text} onChange={(e) => {checkCaptcha(e.target.value); setText(e.target.value)}}/>
{status === false && (<i class='bx bx-x text-color-red'></i>)}
{status === true && (<i class='bx bx-check text-color-green'></i>)}
</div>
</div>
)
}
export default Captcha;
This code is index.jsx
import React from 'react'
import Captcha from '../../../components/Captcha/Captcha';
function Index() {
function change(){
//here i need to call function from Captcha.jsx - resetToken();
}
return (
<div>
<Captcha statusOfCaptcha={resCaptchaData}/>
<button onclick={change}>Reset captcha code</button>
</div>
)
}
export default Index
It would be better to use a custom hook, to store your state, and resetToken function, So you can use it in multiple places.
For more resources about custom hooks.
https://reactjs.org/docs/hooks-custom.html
You can do this in several ways
for example you can use state manager like context api or redux.
In order to have access to your states or functions everywhere and in all pages and components
Or you can put the resetToken function in the parent component and have access to it in the child components.
export const ParentComponent = (children) => {
function resetToken {
....
}
return (
<Recapcha resetToken={resetToken} />
)
}
const Recapcha = ({resetToken}) => {
return (...)
}

Why is the min state getting updated 2 times instead of only once?

Why is the min state getting updated in the multiples of two instead of just updating by one after every 59 seconds? How do I fix it?
import { useRef, useState } from "react";
export const Timer = () => {
const [second, setSecond] = useState(0);
const [min, setMin] = useState(0);
const watch = useRef(null);
const startTimer = () => {
watch.current = setInterval(() => {
setSecond((value) => {
if (value === 59) {
setSecond(0);
setMin((v) => v + 1);
}
return value + 1;
});
}, 1000);
};
return (
<div>
<h1>
{min}:{second}{" "}
</h1>
<button onClick={startTimer}>Start</button>
<button onClick={() => clearInterval(watch.current)}>Pause</button>
<button
onClick={() => {
setSecond(0);
return clearInterval(watch.current);
}}
>
Reset
</button>
</div>
);
};
This is the component as a whole. I am new to react so please help.

React listen to child's state from parent

Damn, two days, two noob questions, sorry guys.
Yesterday, I spent the whole afternoon reading the docs but my fart-ey brain cannot process how to use react hooks to pass data from a child to a parent.
I want to create a button on my parent that can listen to his child's state to check on it and change the background color depending on its value.
Thing is, the child component is mapping some stuff so I cannot create a button (otherwhise it would be rendered multiple times and not only once like I want).
I've thought about moving all the data to my parent component but I cannot understand how since I'm fairly new to React and it's been only two months of learning how to code for me basically.
I will now provide the code for the parent and the child component.
The parent :
import React from "react";
import Quizz from "./components/Quizz";
export default function App() {
const [quizz, setQuizz] = React.useState([]);
React.useEffect(() => {
async function getData() {
const res = await fetch(
"https://opentdb.com/api.php?amount=5&category=27&type=multiple"
);
const data = await res.json();
setQuizz(data.results)
}
getData();
}, []);
function checkOnChild(){ /* <== the function I'd like to use to check on my Quizz component's "activeAnswer" state */
console.log(quizz);
}
const cards = quizz.map((item, key) => {
return <Quizz {...item} key={key}/>;
});
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button> /* <== the button that will use the function */
</div>
);
}
and the child :
import React from "react";
import { useRef } from "react";
export default function Quizz(props) {
const [activeAnswer, setActiveAnswer] = React.useState('');/* <== the state I'd like to check on from my parent component */
function toggle(answer) {
setActiveAnswer(answer);
}
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
let temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
let answers = props.incorrect_answers;
const ref = useRef(false);
if (!ref.current) {
answers.push(props.correct_answer);
shuffleArray(answers);
ref.current = true;
}
const answerDiv = answers.map((answer, key) => (
<div key={key} className="individuals" onClick={()=> toggle(answer)}
style={{background: answer == activeAnswer ? "#D6DBF5" : "transparent" }}>
{answer}
</div>
));
console.log(answers);
console.log(activeAnswer);
console.log(props.correct_answer);
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
);
}
I'm really sorry If it seems dumb or if I'm making forbidden things lmao, but thanks in advance for your help guys!
This should get you a bit further I think.
export default function App() {
const [quizData, setQuizData] = useState([])
const [quizState, setQuizState] = useState({})
useEffect(() => {
async function getData() {
const res = await fetch('https://opentdb.com/api.php?amount=5&category=27&type=multiple')
const data = await res.json()
const results = data.results
setQuizData(results)
setQuizState(results.reduce((acc, curr) => ({ ...acc, [curr.question]: '' }), {}))
}
getData()
}, [])
function checkOnChild() {
console.log(quizState)
}
const cards = quizData.map((item) => {
return <Quizz {...item} key={item.question} quizState={quizState} setQuizState={setQuizState} />
})
return (
<div>
{cards}
<button onClick={checkOnChild}>Check answers</button>
</div>
)
}
export default function Quizz(props) {
function handleOnClick(answer) {
props.setQuizState(prevState => ({
...prevState,
[props.question]: answer,
}))
}
const answers = useMemo(() => {
const arr = [...props.incorrect_answers, props.correct_answer]
return shuffleArray(arr)
}, [props.incorrect_answers, props.correct_answer])
const answerDiv = answers.map((answer) => (
<div
className="individuals"
key={answer}
onClick={() => handleOnClick(answer)}
style={{ background: answer == props.quizState[props.question] ? '#D6DBF5' : 'transparent' }}
>
{answer}
</div>
))
return (
<div className="questions">
<div>
<h2>{props.question}</h2>
</div>
<div className="individuals__container">{answerDiv}</div>
<hr />
</div>
)
}

How to destroy and reinitialize component in react on Button click in another component

I have created a Pagination from a tutorial in React like this,
const [posts, setPosts] = useState([]);
const [showPerPage] = useState(6);
const [pagination, setPagination] = useState({
start: 0,
end: showPerPage
});
const onPaginationChange = (start, end) => {
setPagination({start: start, end: end});
}
The above lines are in the main Component in which I am calling my Pagination component like this:
<Pagination showPerPage={showPerPage} onPaginationChange={onPaginationChange} totalProvider={posts.length}/>
Now I have created the Pagination Component like this,
import React, {useState, useEffect} from 'react'
const Pagination = ({showPerPage, onPaginationChange, totalProvider}) => {
const [counter, setCounter] = useState(1);
useEffect(()=>{
const value = showPerPage * counter;
onPaginationChange(value - showPerPage, value);
},[counter]);
const onButtonClick = (type) => {
if(type==='prev'){
if(counter === 1){
setCounter(1);
}else {
setCounter(counter - 1);
}
}else if( type=== 'next'){
if(Math.ceil(totalProvider/showPerPage)=== counter){
setCounter(counter);
}else {
setCounter(counter + 1);
}
}
}
return (
<div className="paginationWrapper">
<span id="prevBtn" onClick={()=>onButtonClick('prev')} className="disable">Prev.</span>
<div className="numberOfItems">
<span id="startCounter">{counter}</span>of<span>{Math.ceil(totalProvider/showPerPage)}</span>
</div>
<span id="nextBtn" onClick={()=>onButtonClick('next')}>Next</span>
</div>
)
}
export default Pagination
This is the Setup I have, Now I want to reset the Pagination when I click on a button in Parent Component, Because on that button click I need to fetch more or less data, and in this case my pagination always stay on the number where I previously left it,
I can do this 2 ways, but unable to get the code way:
If I can reset the counter (const in Pagination component)to 1, on that button click, then it would be done, Or
If I can just destroy the Component, every State of it, then it would be done.
Please help,
Note: I am not using redux.
I think using ref is the easiest way to do it.
import React, {userRef} from "react"
function ParentComponent() {
const resetRef = useRef();
const handleReset = () => {
if (resetRef && resetReft.current) resetRef.current.resetCounter();
}
return <>
<button onClick={handleReset}>Reset</button>
<Pagination ref={resetRef} showPerPage={showPerPage} onPaginationChange={onPaginationChange} totalProvider={posts.length}/>
</>
}
Add forwardRef and resetCounter() to your Pagination component.
const Pagination = React.forwardRef(({showPerPage, onPaginationChange, totalProvider}, ref) => {
const [counter, setCounter] = useState(1);
useEffect(()=>{
const value = showPerPage * counter;
onPaginationChange(value - showPerPage, value);
},[counter]);
const onButtonClick = (type) => {
if(type==='prev'){
if(counter === 1){
setCounter(1);
}else {
setCounter(counter - 1);
}
}else if( type=== 'next'){
if(Math.ceil(totalProvider/showPerPage)=== counter){
setCounter(counter);
}else {
setCounter(counter + 1);
}
}
}
resetCounter() {
setCounter(1);
}
return (
<div className="paginationWrapper" ref={ref}>
<span id="prevBtn" onClick={()=>onButtonClick('prev')} className="disable">Prev.</span>
<div className="numberOfItems">
<span id="startCounter">{counter}</span>of<span>{Math.ceil(totalProvider/showPerPage)}</span>
</div>
<span id="nextBtn" onClick={()=>onButtonClick('next')}>Next</span>
</div>
)
})
export default Pagination

Resources