How to update the style of an element on state change - reactjs

I have a button that changes state when clicked. I would like a style (resultInfo) to show up on another element when the button state is false. I tried to use useEffect [checkAnswer] to update the other element's inline style, but upon the button's state change, the other element's style is not updated. How come the following doesn't work? Thanks.
// the Continue button that also shows the user if they are correct or wrong
import React, { useEffect, useContext, useState } from 'react';
import { PracticeContext } from '../contexts/PracticeContext';
import ModuleFinished from './ModuleFinished';
// Enables the user to check their answer, shows the result, and provides an element to proceed to the next question
const ModulePracticeAnswerResult = ( {questionNumber, answer, attempt} ) => {
const { setShowModuleFinished } = useContext(PracticeContext);
const { questionIndex, setQuestionIndex } = useContext(PracticeContext);
const { selectedPracticeEnd } = useContext(PracticeContext);
const [checkAnswer, setCheckAnswer] = useState(false);
// create the user selected module for practice
useEffect(() => {
setCheckAnswer(false); // answer state is reverted when question state is updated
}, [questionIndex]);
// navigate to the next question
function progress(e) {
e.preventDefault();
if (checkAnswer === false) {
setCheckAnswer(true);
return;
}
if (selectedPracticeEnd === true) {
// there are no more questions - don't progress any further
if (checkAnswer) {
setShowModuleFinished(true);
}
return;
}
// if checkAnswer is true, user has already answers and received feedback. progress to next question
setQuestionIndex(questionNumber + 1);
}
let resultInfo = { display: 'none' };
useEffect(() => {
// when check answer button has been pressed, its state changes to false until the continue button is pressed
if (checkAnswer === false) {
// display the result of the answer
if (answer === attempt) {
resultInfo = { display: 'block', background: '#4CAF50' }
}
else {
resultInfo = { display: 'block', background: 'rgb(255, 52, 86)' }
}
return;
}
resultInfo = { display: 'none' }; // user hasn't checked the answer yet
}, [checkAnswer]);
return (
<div className="module-practice-answer-result">
<div className="result-info-container">
<div style={resultInfo}>
<p>{ 'result message here...' }</p>
</div>
<button
className={ checkAnswer === false ? 'answer-button answer-button-default' : 'answer-button answer-button-continue' }
onClick={progress} disabled={attempt.length < 1 ? 'disabled' : ''}>
{ checkAnswer === false ? 'Check' : 'Continue' }
</button>
</div>
<ModuleFinished />
</div>
);
}
export default ModulePracticeAnswerResult;

Related

How to imitate hover and active effects onkeydown in react?

Edit:
I have an example on codepen showing the effects I want to implement. It is working, but I'm not sure if it is cleanly done to satisfy React 18's StrictMode.
https://codepen.io/yochess/pen/NWMOvrv?editors=0110
Question:
I have code where I have 2 div elements representing a left arrow and a right arrow.
When I hover over and click the arrow div element, I have css code that changes their styling using hover and active.
//file.css
.left-arrow-wrapper:active {
color: black;
}
.left-arrow-wrapper:hover{
background: rgb(254 226 226) !important;
}
.right-arrow-wrapper:active {
color: black;
}
.right-arrow-wrapper:hover{
background: rgb(254 226 226) !important;
}
I want to sort of emulate this effect with onkeydown.
For example, if the left arrow is clicked then setState is invoked on the left arrow's stylings. 0.1 seconds later using setTimeout, a 2nd setState is invoked on the left arrow and its styling would revert back to its original state.
As a result, useEffect is visited a few times. I want to make sure if a user is spamming the left arrow, then while its styling is changed, I want no effects to take place.
I am new to React and Hooks and the code above works on React 17, but when I change to React 18, the code is bugged. I'm assuming I am incorrectly implementing this effect. Is there a more proper way to accomplish this?
A side question is how do I properly clean up the setTimeouts on unmount? In the code below, I push all the setTimeouts into an array, and then set them to null once they are called. Then on unmount, I would return a function that clears the setTimeouts that are not null.
Here is the code:
import React, { useState, useEffect, useRef } from 'react'
import {
FaAngleLeft,
FaAngleRight,
} from "react-icons/fa"
const setStyles = {
defaultArrowStyle:{
backgroundColor: "lightgray",
borderStyle: "ridge"
},
clickedArrowStyle: {
backgroundColor: "rgb(254 226 226)",
borderStyle: "ridge"
},
}
const ArrowButtons = () => {
const [leftArrowStyle, setLeftArrowStyle] = useState(setStyles.defaultArrowStyle)
const [rightArrowStyle, setRightArrowStyle] = useState(setStyles.defaultArrowStyle)
const timeRef = useRef([])
const counterRef = useRef(0)
useEffect(() => {
window.addEventListener("keydown", handleKey)
return () => {
window.removeEventListener("keydown", handleKey);
}
}, [handleKey])
useEffect(() => {
if (rightArrowStyle.backgroundColor !== setStyles.clickedArrowStyle.backgroundColor) return
const counter = counterRef.current
const timer = setTimeout(() => cacheAndSetArrowStyle(setRightArrowStyle, counter), 100)
timeRef.current[counterRef.current++] = timer
}, [rightArrowStyle])
useEffect(() => {
if (leftArrowStyle.backgroundColor !== setStyles.clickedArrowStyle.backgroundColor) return
const counter = counterRef.current
const timer = setTimeout(() => cacheAndSetArrowStyle(setLeftArrowStyle, counter), 100)
timeRef.current[counterRef.current++] = timer
}, [leftArrowStyle])
useEffect(() => {
return () => {
// eslint-disable-next-line
timeRef.current.filter(timer => timer).forEach(timer => clearTimeout(timer))
}
}, [])
return (
<div className="row">
<div className="col-2 hand-icon text-center left-arrow-wrapper" style={leftArrowStyle} onClick={handleLeftClick}>
<FaAngleLeft className="left-arrow" />
</div>
<div className="col-2 hand-icon text-center right-arrow-wrapper" style={rightArrowStyle} onClick={handleRightClick}>
<FaAngleRight className="double-right-arrow" />
</div>
</div>
)
function handleKey(event) {
if (event.key === "ArrowRight") {
setRightArrowStyle(setStyles.clickedArrowStyle)
}
if (event.key === "ArrowLeft") {
setLeftArrowStyle(setStyles.clickedArrowStyle)
}
}
function cacheAndSetArrowStyle(setArrowStyle, counter) {
timeRef.current[counter] = null
setArrowStyle(setStyles.defaultArrowStyle)
}
}
export default React.memo(ArrowButtons)

React toggle tri-state component

I've came across this, which helped me this as far as I've got. Though, I'm trying to hack together a simple status component that instead of a checkbox, is just a div with styling to make it appear as a tiny dot that toggles between three strings, offline, wip and online onClick! Changing just the color upon change of state. (Practically speaking, I'll set an array of objects as offline and if toggled differently I'll store that preference.)
I'm just stuck trying to move away from a checkbox, I'll show you what I mean:
const STATUS_STATES = {
Online: "online",
Wip: "wip",
Offline: "offline",
};
function SomePage() {
const [status, setStatus] = useState(STATUS_STATES.Offline);
const handleChange = () => {
let updatedChecked;
if (status === STATUS_STATES.Online) {
updatedChecked = STATUS_STATES.Offline;
} else if (status === STATUS_STATES.Offline) {
updatedChecked = STATUS_STATES.Wip;
} else if (status === STATUS_STATES.Wip) {
updatedChecked = STATUS_STATES.Online;
}
setStatus(updatedChecked);
};
const Status = ({ value, onChange }) => {
const checkboxRef = React.useRef();
useEffect(() => {
if (value === STATUS_STATES.Online) {
console.log("online")
checkboxRef.current.checked = true;
checkboxRef.current.indeterminate = false;
} else if (value === STATUS_STATES.Offline) {
console.log("offline")
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = false;
} else if (value === STATUS_STATES.Wip) {
console.log("wip")
checkboxRef.current.checked = false;
checkboxRef.current.indeterminate = true;
}
}, [value]);
return (
<label>
<input ref={checkboxRef} type="checkbox" onChange={onChange} />
{/* I need to replace the line above with the line below */}
{/* while escaping the label element it's wrapped within */}
<div
className={
(value === "offline" && "offline") ||
(value === "wip" && "wip") ||
(value === "online" && "online")
}
onChange={onChange}
/>
</label>
);
};
return (
<>
<Status value={status} onChange={handleChange} />
<p>Is checked? {status}</p>
</>
)
}
.status{
width: 6px;
height: 6px;
background: #ee4f4f;
border-radius: 50%;
}
Any advice to approach this more efficiently?
This tidies it up quite a bit
codebox: https://codesandbox.io/s/intelligent-gareth-7qvko?file=/src/App.js:0-1050
import "./styles.css";
import React, { useState, useEffect } from "react";
const STATUS_STATES = ["Online", "Wip", "Offline"];
const STATUS_COLORS = ["green", "orange", "red"];
const Dot = ({ color, onClick }) => (
<div
onClick={onClick}
style={{ borderRadius: 5, height: 10, width: 10, background: color }}
/>
);
const Main = () => {
const [status, setStatus] = useState(0);
const handleClick = () => {
const newStatus = (status + 1) % STATUS_STATES.length;
setStatus(newStatus);
};
const text = STATUS_STATES[status];
return (
<>
<Dot onClick={handleClick} color={STATUS_COLORS[status]} />
<div onClick={handleClick}>{text}</div>
</>
);
};
export default Main;
You will see to loop through the statuses, I have put them through in an array, and used the remainder operator to get the next index.
The useEffect logic only needed a simple if for each value which has been written in short hand which (I think) is more readable for variables.
You will also notice the onclick on the checkbox input is wrapped in a timeout with a 0ms wait. This is a workaround because I couldnt prevent the default checkbox behaviour from happening. This ensures the click handling is run after the native logic.
I also reduced your array to just an array of strings - This was just to simplify the example.

React hangman game: clicked state not managed correctly producing unexpected behavior

Hello I have Letters.js which generates AvailableLetter for a-z.
import React, {useState} from 'react';
import AvailableLetter from './AvailableLetter/AvailableLetter';
import classes from './Letters.module.css';
const Letters = (props) => {
const [allLetters]=useState(
['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
);
const playHandler = (alphabet) => {
const solution = props.solution.split('');
console.log(solution);
if (solution.indexOf(alphabet)<0)
{
console.log('incorrect');
return false;
}
else
{
console.log('correct');
return true;
}
}
const availableLetters = [ ...allLetters ].map(
(alphabet,i) => {
return (
<AvailableLetter setSolved={props.setSolved} play={()=>playHandler(alphabet)} correct={()=>props.correct(alphabet)} incorrect={()=>props.incorrect(alphabet)} solution={props.solution} key={i} alphabet={alphabet} />
);
}
);
return (
<div className={classes.Letters}>
<p>Solution: {props.solution}</p>
<div className={classes.AvailableLetters}>
{availableLetters}
</div>
</div>
);
}
export default Letters;
I have AvailableLetter.js here and I want it to be unclickable after first time clicked.
import React, {useState, useEffect} from 'react';
import classes from './AvailableLetter.module.css';
import Ax from '../../hoc/Ax';
const AvailableLetter = (props) => {
// const [show,setShow]=useState(true);
// const [clicked, setClicked]=useState(false);
// const [outcome,setOutcome]=useState(false);
const [clicked,setClicked]=useState(false);
// if (show)
// {
// setClicked(true);
// }
// const play = (alphabet) => {
// const solution = props.solution.split('');
// if (solution.indexOf(alphabet)<0)
// {
// return false;
// }
// else
// {
// return true;
// }
// }
const setStuff = () => {
// setShow(true);
setClicked(false);
props.setSolved();
};
useEffect( setStuff,[clicked] );
// useEffect( ()=>setShow(true),[show] );
// useEffect( ()=>props.setSolved(),[show] );
if (clicked) // STRANGELY THIS PART WORKS!!!
{
if (props.play())
{
props.correct();
// alert('correct');
}
else
{
props.incorrect();
// alert('wrong');
}
}
const attachedClasses = [classes.AvailableLetter];
const disableLetter = () => {
attachedClasses.push(classes.Disabled);
setClicked(true);
};
// const letter = <span onClick={disableLetter} className={attachedClasses.join(' ')} >{props.alphabet}</span>;
let letter=null;
if (!clicked)
{
letter = <span onClick={disableLetter} className={attachedClasses.join(' ')} >{props.alphabet}</span>;
}
else if(clicked) // CODE NEVER GETS HERE!!!!!!!!!!!!!!
{
letter = <span className={attachedClasses.join(' ')} >{props.alphabet}</span>;
}
return (
<Ax>
{letter}
</Ax>
);
}
export default AvailableLetter;
The CSS file for it is AvailableLetter.module.css:
.AvailableLetter
{
border: 1px solid black;
padding: 10px;
margin: 3px;
}
.AvailableLetter.Disabled
{
pointer-events: none;
background: #aaa;
}
It seems my logic inside AvailableLetter is correct, but it never reaches the else if (clicked) part and letters remain always clickable.
Inside AvailableLetter.js: If I use button instead:
<button disable={clicked} onClick={()=>setClicked(true)}>props.alphabet</button>
Strangely disable doesn't work even when setClicked(true).
But if I do
<button disable>props.alphabet</button>
Now it disables.
I appreciate your help!
Update:
Removing setClicked(false) from setStuff() gets error:
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
Your combination of effects and hooks have created a feedback loop.
This button:
<span onClick={disableLetter} className={attachedClasses.join(' ')} >{props.alphabet}</span>
calls this function:
const disableLetter = () => {
attachedClasses.push(classes.Disabled);
setClicked(true);
};
which sets clicked to true. Once that happens, this effect runs:
const setStuff = () => {
// setShow(true);
setClicked(false);
props.setSolved();
};
useEffect( setStuff,[clicked] );
which immediately makes clicked == false again. Also worth noting that setStuff gets called a second time because clicked changed values, triggering the effect again. What is setStuff supposed to do in this context? Remove the call to setClicked(false) in that function and clicked should remain as true.
I'd highly recommend cleaning up your code so it's easier to follow. Logic like this:
if (!clicked) {
// not clicked
} else if (clicked) {
// clicked
}
could easily be described like this:
if (clicked) {
// clicked
} else {
// not clicked
}
By doing this, you'll save yourself a lot of headaches when debugging problems like the one you're having.
Maximum Update Depth Error
Based on your stack trace and code in the question/pastebin, you have another loop caused by this part:
if (clicked)
{
if (props.play())
{
props.correct();
// alert('correct');
}
else
{
props.incorrect(); // <- this is your trigger
// alert('wrong');
}
}
You should move this code into your setStuff function so it's called only once by the effect.
I'd also suggest re-thinking your structure here, so you (and others) can follow what you're doing better. Stack traces will help you with any further errors you get so you can follow the source of more loops you might encounter.

Issues when displaying records from database using React Redux

For about 7 hours now, Am working with infinite Scrolling using react redux.
This code works very well by displaying 20 database records as user scroll-down the page. But am currently face with two issues.
1.) I cannot get the application to display a message "No more records" once record gets finished displaying from database.
I have tried
get finished() {
console.log(this.props.users_scroll.length);
if (this.row >= this.props.users_scroll.length ) {
return (<li key={'done'}>No More Message to Load.</li>);
}
return null;
}
but console give values undefined for this line of code
console.log(this.props.users_scroll.length);
I have also tried
get finished() {
console.log(this.loadMore.length);
if (this.row >= this.loadMore.length ) {
return (<li key={'done'}>No More Message to Load.</li>);
}
return null;
}
but console give values 0 for this line of code console.log(this.loadMore.length); as a result the application will immediately
shows No more records whereas they are still about 15 records in the database.
2.) When all the 20 records from database gets display, there is still continuous Call from server/database as long as the user
keeps on scrolling down the Page. I have tried this but it does not stop the unecessary Server/database Calls. it seems that this.users_scroll
is either empty or undefined
if(this.users_scroll !='' ){
loadMore();
}
Here is the code
import React, { Component, Fragment } from "react";
import { render } from "react-dom";
import request from "superagent";
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { userActions } from '../_actions';
class InfinitescrollPage extends React.Component {
constructor(props) {
super(props);
// Sets up our initial state
this.state = {
users_scroll: [],
};
//set parameters
this.row = 0;
this.rowperpage = 5;
this.cText = "";
this.loadMore = this.loadMore.bind(this);
// Binds our scroll event handler
window.onscroll = () => {
const {
loadMore,
state: {
},
} = this;
// Checks that the page has scrolled to the bottom
if (
window.innerHeight + document.documentElement.scrollTop
=== document.documentElement.offsetHeight
) {
//if(this.users_scroll !='' ){
loadMore();
//}
}
};
}
componentWillMount() {
// Loads some users on initial load
this.props.dispatch(userActions.getAll_Infinitescroll(this.row));
}
loadMore() {
this.cText = "content is Loading ...";
this.row+=this.rowperpage;
setTimeout(()=>{
this.len =this.props.dispatch(userActions.getAll_Infinitescroll(this.row));
this.cText = "";
},100);
}
get finished() {
//alert('database rows lengths ' +this.props.users_scroll.length);
//if (this.row >= this.loadMore.length ) {
if (this.row >= this.props.users_scroll.length ) {
return (<li key={'done'}>No More Message to Load.</li>);
}
return null;
}
render() {
//const {} = this.state;
const {user_scroll, users_scroll } = this.props;
return (
<div>
<h1>Infinite Users!</h1>
<p>Scroll down to load more!!</p>
{users_scroll.items2 && users_scroll.items2.map(user_scroll => (
<Fragment key={user_scroll.username}>
<hr />
<div style={{ display: 'flex' }}>
<img
alt={user_scroll.username}
src='http://localhost/apidb_react/2.png'
style={{
borderRadius: '50%',
height: 72,
marginRight: 20,
width: 72,
}}
/>
<div>
<h2 style={{ marginTop: 0 }}>
#{user_scroll.uuid}
</h2>
<p>Name: {user_scroll.name}</p>
<p>Email: {user_scroll.email}</p>
<p>Counting: {user_scroll.rcount}</p>
</div>
</div>
</Fragment>
))}
{this.finished}
{this.cText}
<input type="text" className="form-control" name="this.row" id="this.row" value={this.row} onChange={this.handleChange} />
<hr />
</div>
);
}
}
const container = document.createElement("div");
document.body.appendChild(container);
render(<InfinitescrollPage />, container);
users.service
function getAll_Infinitescroll(us) {
const request = {
method: 'POST',
body: JSON.stringify({us});
};
return fetch(`/users.php`, request).then(handleResponse)
.then(users_scroll => {
if (users_scroll) {
console.log(users_scroll);
}
return users_scroll;
});
}
This has been resolved. I need to get the row counts from the database and the compare it with the passing row data.
1.) To display No more message it becomes
this.count='20';
or
this.count=this.props.dispatch(userActions.getRowCount());
get finished() {
if (this.row >= this.count ) {
return (<li key={'done'}>No More Message to Load.</li>);
}
return null;
}
2.) To prevent unnecessary scrolling when there is no more data to display
loadMore() {
if (this.row != this.count ) {
this.cText = "content is Loading ...";
this.row+=this.rowperpage;
setTimeout(()=>{
this.len =this.props.dispatch(userActions.getAll_Infinitescroll(this.row));
this.cText = "";
},100);
}
}

Select row on click react-table

I am trying to find the best table to use with my react apps, and for now, the react-table offers everything I need (pagination, server-side control, filtering, sorting, footer row).
This being said, I can't seem to be able to select a row. There are no examples that show this.
Some things, that I have tried include trying to set a className on click of the row. But I can't seem to find the calling element in e nor t. Also, I don't like this approach, because it is not how a react app should do things.
<ReactTable
...
getTrProps={(state, rowInfo, column, instance) => {
return {
onClick: (e, t) => {
t.srcElement.classList.add('active')
},
style: {
}
}
}}
/>
Some possible workaround would be to render checkboxes as a first column, but this is not optimal as it limits the area to click to 'activate' the row. Also, the visual feedback will be less expressive.
Am I missing the elephant in the room? And if not, do you know another library that supports the things that I've described earlier?
Thank you!
EDIT:
Another option, this being open source, is to suggest an edit. And maybe this is the proper thing to do.
EDIT 2
Another thing, suggested by Davorin RuĆĄevljan in the comments, but I couldn't make it work was:
onRowClick(e, t, rowInfo) {
this.setState((oldState) => {
let data = oldState.data.slice();
let copy = Object.assign({}, data[rowInfo.index]);
copy.selected = true;
copy.FirstName = "selected";
data[rowInfo.index] = copy;
return {
data: data,
}
})
}
....
getTrProps={(state, rowInfo, column) => {
return {
onClick: (e, t) => { this.onRowClick(e, t, rowInfo) },
style: {
background: rowInfo && rowInfo.row.selected ? 'green' : 'red'
}
}
}}
This sets the 'FirstName' column to 'selected', but does not set the class to 'green'
I found the solution after a few tries, I hope this can help you. Add the following to your <ReactTable> component:
getTrProps={(state, rowInfo) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e) => {
this.setState({
selected: rowInfo.index
})
},
style: {
background: rowInfo.index === this.state.selected ? '#00afec' : 'white',
color: rowInfo.index === this.state.selected ? 'white' : 'black'
}
}
}else{
return {}
}
}
In your state don't forget to add a null selected value, like:
state = { selected: null }
There is a HOC included for React-Table that allows for selection, even when filtering and paginating the table, the setup is slightly more advanced than the basic table so read through the info in the link below first.
After importing the HOC you can then use it like this with the necessary methods:
/**
* Toggle a single checkbox for select table
*/
toggleSelection(key: number, shift: string, row: string) {
// start off with the existing state
let selection = [...this.state.selection];
const keyIndex = selection.indexOf(key);
// check to see if the key exists
if (keyIndex >= 0) {
// it does exist so we will remove it using destructing
selection = [
...selection.slice(0, keyIndex),
...selection.slice(keyIndex + 1)
];
} else {
// it does not exist so add it
selection.push(key);
}
// update the state
this.setState({ selection });
}
/**
* Toggle all checkboxes for select table
*/
toggleAll() {
const selectAll = !this.state.selectAll;
const selection = [];
if (selectAll) {
// we need to get at the internals of ReactTable
const wrappedInstance = this.checkboxTable.getWrappedInstance();
// the 'sortedData' property contains the currently accessible records based on the filter and sort
const currentRecords = wrappedInstance.getResolvedState().sortedData;
// we just push all the IDs onto the selection array
currentRecords.forEach(item => {
selection.push(item._original._id);
});
}
this.setState({ selectAll, selection });
}
/**
* Whether or not a row is selected for select table
*/
isSelected(key: number) {
return this.state.selection.includes(key);
}
<CheckboxTable
ref={r => (this.checkboxTable = r)}
toggleSelection={this.toggleSelection}
selectAll={this.state.selectAll}
toggleAll={this.toggleAll}
selectType="checkbox"
isSelected={this.isSelected}
data={data}
columns={columns}
/>
See here for more information:
https://github.com/tannerlinsley/react-table/tree/v6#selecttable
Here is a working example:
https://codesandbox.io/s/react-table-select-j9jvw
I am not familiar with, react-table, so I do not know it has direct support for selecting and deselecting (it would be nice if it had).
If it does not, with the piece of code you already have you can install the onCLick handler. Now instead of trying to attach style directly to row, you can modify state, by for instance adding selected: true to row data. That would trigger rerender. Now you only have to override how are rows with selected === true rendered. Something along lines of:
// Any Tr element will be green if its (row.age > 20)
<ReactTable
getTrProps={(state, rowInfo, column) => {
return {
style: {
background: rowInfo.row.selected ? 'green' : 'red'
}
}
}}
/>
if u want to have multiple selection on select row..
import React from 'react';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import { ReactTableDefaults } from 'react-table';
import matchSorter from 'match-sorter';
class ThreatReportTable extends React.Component{
constructor(props){
super(props);
this.state = {
selected: [],
row: []
}
}
render(){
const columns = this.props.label;
const data = this.props.data;
Object.assign(ReactTableDefaults, {
defaultPageSize: 10,
pageText: false,
previousText: '<',
nextText: '>',
showPageJump: false,
showPagination: true,
defaultSortMethod: (a, b, desc) => {
return b - a;
},
})
return(
<ReactTable className='threatReportTable'
data= {data}
columns={columns}
getTrProps={(state, rowInfo, column) => {
return {
onClick: (e) => {
var a = this.state.selected.indexOf(rowInfo.index);
if (a == -1) {
// this.setState({selected: array.concat(this.state.selected, [rowInfo.index])});
this.setState({selected: [...this.state.selected, rowInfo.index]});
// Pass props to the React component
}
var array = this.state.selected;
if(a != -1){
array.splice(a, 1);
this.setState({selected: array});
}
},
// #393740 - Lighter, selected row
// #302f36 - Darker, not selected row
style: {background: this.state.selected.indexOf(rowInfo.index) != -1 ? '#393740': '#302f36'},
}
}}
noDataText = "No available threats"
/>
)
}
}
export default ThreatReportTable;
The answer you selected is correct, however if you are using a sorting table it will crash since rowInfo will became undefined as you search, would recommend using this function instead
getTrGroupProps={(state, rowInfo, column, instance) => {
if (rowInfo !== undefined) {
return {
onClick: (e, handleOriginal) => {
console.log('It was in this row:', rowInfo)
this.setState({
firstNameState: rowInfo.row.firstName,
lastNameState: rowInfo.row.lastName,
selectedIndex: rowInfo.original.id
})
},
style: {
cursor: 'pointer',
background: rowInfo.original.id === this.state.selectedIndex ? '#00afec' : 'white',
color: rowInfo.original.id === this.state.selectedIndex ? 'white' : 'black'
}
}
}}
}
If you are using the latest version (7.7 at the time) it is possible to select rows using toggleRoWSelected() see full example;
<tr
{...row.getRowProps()}
className="odd:bg-white even:bg-gray-100"
onClick={() => row.toggleRowSelected()}
>
{row.cells.map((cell) => {
return (
<td {...cell.getCellProps()} className="p-2">
{cell.render("Cell")}
</td>
);
})}
</tr>;
Another mechanism for dynamic styling is to define it in the JSX for your component. For example, the following could be used to selectively style the current step in the React tic-tac-toe tutorial (one of the suggested extra credit enhancements:
return (
<li key={move}>
<button style={{fontWeight:(move === this.state.stepNumber ? 'bold' : '')}} onClick={() => this.jumpTo(move)}>{desc}</button>
</li>
);
Granted, a cleaner approach would be to add/remove a 'selected' CSS class but this direct approach might be helpful in some cases.
Multiple rows with checkboxes and select all using useState() hooks. Requires minor implementation to adjust to own project.
const data;
const [ allToggled, setAllToggled ] = useState(false);
const [ toggled, setToggled ] = useState(Array.from(new Array(data.length), () => false));
const [ selected, setSelected ] = useState([]);
const handleToggleAll = allToggled => {
let selectAll = !allToggled;
setAllToggled(selectAll);
let toggledCopy = [];
let selectedCopy = [];
data.forEach(function (e, index) {
toggledCopy.push(selectAll);
if(selectAll) {
selectedCopy.push(index);
}
});
setToggled(toggledCopy);
setSelected(selectedCopy);
};
const handleToggle = index => {
let toggledCopy = [...toggled];
toggledCopy[index] = !toggledCopy[index];
setToggled(toggledCopy);
if( toggledCopy[index] === false ){
setAllToggled(false);
}
else if (allToggled) {
setAllToggled(false);
}
};
....
Header: state => (
<input
type="checkbox"
checked={allToggled}
onChange={() => handleToggleAll(allToggled)}
/>
),
Cell: row => (
<input
type="checkbox"
checked={toggled[row.index]}
onChange={() => handleToggle(row.index)}
/>
),
....
<ReactTable
...
getTrProps={(state, rowInfo, column, instance) => {
if (rowInfo && rowInfo.row) {
return {
onClick: (e, handleOriginal) => {
let present = selected.indexOf(rowInfo.index);
let selectedCopy = selected;
if (present === -1){
selected.push(rowInfo.index);
setSelected(selected);
}
if (present > -1){
selectedCopy.splice(present, 1);
setSelected(selectedCopy);
}
handleToggle(rowInfo.index);
},
style: {
background: selected.indexOf(rowInfo.index) > -1 ? '#00afec' : 'white',
color: selected.indexOf(rowInfo.index) > -1 ? 'white' : 'black'
},
}
}
else {
return {}
}
}}
/>
# react-table with edit button #
const [rowIndexState, setRowIndexState] = useState(null);
const [rowBackGroundColor, setRowBackGroundColor] = useState('')
{...row.getRowProps({
onClick: (e) => {
if (!e.target.cellIndex) {
setRowIndexState(row.index);
setRowBackGroundColor('#f4f4f4')
}
},
style: {
background: row.index === rowIndexState ? rowBackGroundColor : '',
},
})}

Resources