React event.target setState. Set list item backgroundColor - reactjs

After clicking on the item list I want to change its background color. After clicking on another, I want the color to return to the default. I did something like that:
import React, {useState} from 'react'
function Node({expanded, name}) {
const [targetS, setTargetS] = useState()
const Select = (element) => {
const {target} = element
targetS && targetS.backgroundColor = ''
setTargetS(target)
targetS.style.backgroundColor = 'orange'
}
return (
<li onClick={Select}>
{expanded? '-':'+'} {name}
</li>
)
}
export default Node
but it doesn't work as I think

If you're using React, take advantage of writing JavaScript inside the JSX and React state. You don't need to manipulate the color of your element using event object directly. (The way you are doing it needs some correction also). You can let React be responsible for triggering the color change by leveraging react state to act as a toggle for your li className.
Create a activeNode state and its setter setActiveNode in parent component of Node. Then pass it as follows:-
<Node name={name} expanded={expanded} activeNode={activeNode} setActiveNode={setActiveNode}/>
import React, {useState} from 'react'
function Node({expanded, name, setActiveNode, activeNode}) {
const selectElement = (name) => {
setActiveNode(name)
}
return (
<li className={activeNode===name?"orange":""} onClick={()=>selectElement(name)}>
{expanded? '-':'+'} {name}
</li>
)
}
export default Node
Inside your stylesheet:-
orange{
background:"orange"
}

Related

Need to re-render component after state is set to true

In my react app, I have a page that allows the user to "add" exercises to a personal list. The option to add an exercise is included in one "page" and the ability to see added exercises is view in a other. Side by side showing the two pages, notice 'Your Exercises' is empty. What I am trying to do is display these selected exercises. The exercises themselves are loaded via mapping function, from an array of objects. each object has a "selected" field and are set as "false" by default. My "add" button in the exercises page changes the state value of each component to "true", so now what I want to do is get these "true" values and render them on the page where it should show your exercises.
//Render your exercises
import React from "react";
import ExListItem from "./ExListItem";
// import selectedExercises from "../../../selectedExercises";
import exercises from "../../../exercises";
const selectedExerciseList = exercises
.filter(function (item) {
return item.selected === true;
})
.map(function ({name, reps}) {
return {name, reps};
});
// MAP HERE
function createSelectedExercises(exercises) {
return <ExListItem name={exercises.name} reps={exercises.reps} />;
}
function ExerciseList() {
return (
<ul data-tag="channelList" id="exercise-list" class="list-group mb-3">
{selectedExerciseList.map(createSelectedExercises)}
</ul>
);
}
export default ExerciseList;
Shouldnt this map the exercises?
You may need to use React state to accomplish this! You can do so with the following:
import React, { useState, useEffect } from 'react'
const Component = () => {
const [exercises, setExercises] = useState([])
useEffect(() => {
setExercises(exerciseFilterFunction(exercises)) // this will cause the re-render
}, [])
return (
<div>{exercises.map(e => <div>{e.name}</div>)}</div>
)
}

Inject Props to React Component

For security reasons, I have to update ant design in my codebase from version 3 to 4.
Previously, this is how I use the icon:
import { Icon } from 'antd';
const Demo = () => (
<div>
<Icon type="smile" />
</div>
);
Since my codebase is relatively big and every single page use Icon, I made a global function getIcon(type) that returns <Icon type={type}>, and I just have to call it whenever I need an Icon.
But starting from antd 4, we have to import Icon we want to use like this:
import { SmileOutlined } from '#ant-design/icons';
const Demo = () => (
<div>
<SmileOutlined />
</div>
);
And yes! Now my getIcon() is not working, I can't pass the type parameter directly.
I tried to import every icon I need and put them inside an object, and call them when I need them. Here's the code:
import {
QuestionCircleTwoTone,
DeleteOutlined,
EditTwoTone
} from '#ant-design/icons';
let icons = {
'notFound': <QuestionCircleTwoTone/>,
'edit': <EditTwoTone/>,
'delete': <DeleteOutlined/>,
}
export const getIcon = (
someParam: any
) => {
let icon = icons[type] !== undefined ? icons[type] : icons['notFound'];
return (
icon
);
};
My problem is: I want to put someParam to the Icon Component, how can I do that?
Or, is there any proper solution to solve my problem?
Thanks~
You can pass props as follows in the icons Object:
let icons = {
'notFound':(props:any)=> <QuestionCircleTwoTone {...props}/>,
'edit': (props:any)=><EditTwoTone {...props}/>,
'delete':(props:any)=> <DeleteOutlined {...props}/>,
}
And then if you will pass any prop to the Icon component then it will pass the prop to the specific icon component
let Icon = icons[type] !== undefined ? icons[type] : icons['notFound'];
return (<Icon someParam={'c'}/>)

How should I update individual items' className onClick in a list in a React functional component?

I'm new to React and I'm stuck trying to get this onClick function to work properly.
I have a component "Row" that contains a dynamic list of divs that it gets from a function and returns them:
export function Row({parentState, setParentState}) {
let divList = getDivList(parentState, setParentState);
return (
<div>
{divList}
</div>
)
}
Say parentState could just be:
[["Name", "info"],
["Name2", "info2"]]
The function returns a list of divs, each with their own className determined based on data in the parentState. Each one needs to be able to update its own info in parentState with an onClick function, which must in turn update the className so that the appearance of the div can change. My code so far seems to update the parentState properly (React Devtools shows the changes, at least when I navigate away from the component and then navigate back, for some reason), but won't update the className until a later event. Right now it looks like this:
export function getDivList(parentState, setParentState) {
//parentState is an array of two-element arrays
const divList = parentState.map((ele, i) => {
let divClass = "class" + ele[1];
return (
<div
key={ele, i}
className={divClass}
onClick={() => {
let newParentState =
JSON.parse(JSON.stringify(parentState);
newParentState[i][1] = "newInfo";
setParentState(newParentState);}}>
{ele[0]}
</div>
)
}
return divList;
}
I have tried to use useEffect, probably wrong, but no luck. How should I do this?
Since your Row component has parentState as a prop, I assume it is a direct child of this parent component that contains parentState. You are trying to access getDivList in Row component without passing it as a prop, it won't work if you write your code this way.
You could use the children prop provided by React that allow you to write a component with an opening and closing tag: <Component>...</Component>. Everything inside will be in the children. For your code it would looks like this :
import React from 'react';
import { render } from 'react-dom';
import './style.css';
const App = () => {
const [parentState, setParentState] = React.useState([
['I am a div', 'bg-red'],
['I am another div', 'bg-red'],
]);
React.useEffect(
() => console.log('render on ParentState changes'),
[parentState]
);
const getDivList = () => {
return parentState.map((ele, i) => {
return (
<div
key={(ele, i)}
className={ele[1]}
onClick={() => {
// Copy of your state with the spread operator (...)
let newParentState = [...parentState];
// We don't know the new value here, I just invented it for the example
newParentState[i][1] = [newParentState[i][1], 'bg-blue'];
setParentState(newParentState);
}}
>
{ele[0]}
</div>
);
});
};
return <Row>{getDivList()}</Row>;
};
const Row = ({ children }) => {
return <>{children}</>;
};
render(<App />, document.getElementById('root'));
And a bit of css for the example :
.bg-red {
background-color: darkred;
color: white;
}
.bg-blue {
background-color:aliceblue;
}
Here is a repro on StackBlitz so you can play with it.
I assumed the shape of the parentState, yu will have to adapt by your needs but it should be something like that.
Now, if your data needs to be shared across multiple components, I highly recommand using a context. Here is my answer to another post where you'll find a simple example on how to implement a context Api.

useState not updating[NOT AN ASYNC ISSUE]

I'm new to react hooks so I'm practicing with showing and hiding a div when checking and unckecking a checkbox input. The problem is that the state updates on the main file where I have the function that handles it but in the file where I actually have the div it does not update so it does not hide or display.
File that handles the change of the state:
import {react, useState} from "react";
export const Checker = () => {
const [checked, setChecked] = useState(true)
const clickHandler = () => {
setChecked(!checked)
console.log(checked)
}
return {checked, clickHandler, setChecked}
}
File where the checkbox is located:
import React from "react";
import { Extras, Wrapper } from "./extras.styles";
import { Checker } from "../hooks/useCheckboxes";
const Extra = () => {
const {checked, setChecked, clickHandler} = Checker()
return <>
<Extras>
<Wrapper>
<input type= 'checkbox' onClick={clickHandler} checked = {checked} onChange={e => setChecked(e.target.checked)}></input>
</Wrapper>
</Extras>
</>
}
export default Extra;
File that contains the div i want to display and hide dynamically:
import house from '../../icons/house.png'
import { Wrapper } from "./foto.styles";
import { Checker } from "../hooks/useCheckboxes";
import { Inside, Middle} from "./foto.styles";
const Home = () => {
const {checked} = Checker()
return <>
<Wrapper>
<Inside>
<Middle>
{checked && <House src={house}/>}
</Middle>
</Inside>
</Wrapper>
</>
}
export default Home;
Some issues are:
Checker looks like you want it to be a custom hook, not a React component, so it should be called useChecker or something like that, not Checker
You have both a change handler and a click handler. You should only have one. If you want the new state to come from the checkbox, you should use e.target.checked. If you want the new state to flip the old state, use the clickHandler you defined in Checker.
You only need a fragment when enclosing multiple elements. If you only have one, you don't need a fragment.
Because state setters don't update the variable immediately, your console.log(checked) won't display the new value, but the old value - if you want to log the new value when it changes, use useEffect with a dependency array of [checked] instead.
const Extra = () => {
const { checked, clickHandler } = useChecker()
return (
<Extras>
<Wrapper>
<input type='checkbox'checked={checked} onChange={clickHandler} />
</Wrapper>
</Extras>
)
}
use it like that
const {checked, clickHandler, setChecked} = Checker()
Or if you want to be able to make custom names then you need to use an array instead of an object.
the function return value.
return [checked, clickHandler, setChecked]
the function call
const [checked, setChecked, clickHandler] = Checker()
and for convention follow react hooks naming rules by renaming the function to useChecker() instead of Checker()

React Hooks: State Is Not Updating in Function

I have spent several hours trying to fix this issue with no luck. I have a reusable component that starts with a state of any empty object, the object is given a series of properties based off of props, and then based on user input on each set of radio buttons will show a child component.
Unfortunately, the function that is supposed to update the state that will then trigger whether the user sees the child component is not working.
I believe the issue is in my editDisplay function. Currently when I click one of the radio buttons the new object that should become the new state is logged to the console correctly, but when I use React Devtools to inspect the page, I see that state is not updating. That being said, I could see a case where I am misunderstanding useEffect, and perhaps useEffect is running each time editDisplay is running. Any help would be appreciated.
import React, {useState, useEffect} from 'react'
import {Form} from 'react-bootstrap'
import RatingInput from './RatingInput'
export default function MembershipForm({inputs, name, record, setRecord}) {
const [display, setDisplay] = useState({})
useEffect(()=>{
inputs.forEach(input => {
var newDisplay = display
newDisplay[name+input]=false
setDisplay(newDisplay)})
},[])
const editDisplay = (input, visible) => {
var newDisplay =display
newDisplay[name+input]=visible
console.log(newDisplay)
setDisplay(newDisplay)
}
const questions=inputs.map(input => {
return (
<div key={name+input}>
<Form.Group>
<Form.Label className='mx-2'>Do you have a {name} {input} Rating?</Form.Label>
<Form.Check inline label='Yes' value={true} name={name+input} type='radio' className = 'mx-2' onClick ={() =>{editDisplay(input, true)}} />
<Form.Check inline label='No' value ={false} name={name+input} type='radio' className = 'mx-2' defaultChecked onClick = {() =>{editDisplay(input, false)}} />
</Form.Group>
{display[name+input] && <RatingInput input={input}/>}
</div >
)
})
return (
<div className='ml-3'>
{questions}
</div>
)
}
const editDisplay = (input, visible) => {
var newDisplay = display
newDisplay[name+input]=visible
console.log(newDisplay)
setDisplay(newDisplay)
}
You should perform react state update in an immutable way.
What you are doing above is you are mutating existing state variable (that assignment on first line doesn't add much: newDisplay refers to same object as display). In such case react might not detect change. Do this instead:
var newDisplay = { ... display };

Resources