set hook with temporary object - reactjs

i expect the hook to be updated(and i want every time to keep only one object i dont want to save previous value)
my hooks:
const [rooms, setrooms] = useState([]);
const [roomFilter,setRoomFilter]=useState([])
function to update roomFilter hook:
const deleteR=(i)=>{
var temp=rooms.filter((element,index)=>(index==i))
setRoomFilter([temp[0].Name])
somehow it shows nothing in roomFilter hook after update
i have tried also initiakize my hook like this:
const [roomFilter,setRoomFilter]=useState({})
i dont understand why
edit
i am using function deleteR like this:
<Route exact path="/"component={() => { return <HomeScreen rooms={rooms} filter={deleteR} room={roomFilter} /> }}
from HomeScreen component i have two more components :
HomeScreenRooms and Room
function roomFilter is been sent from app to HomeSCreen >to HomeScreenRooms
HomeScreen:
<HomeScreenRooms rooms={props.rooms} filter={props.filter} room={props.roomFilter} />
HomeScreenRooms:
{props.rooms.map((element) => {
return ( <div style={{ width: "100px",height: "100px",position: "relative",
left: "-150px",top: "-100px",display: "inline" }}>
<Link to="/room"><button onClick={() => {props.filter(element.index) }} style={{ width: "116px",height: "116px",backgroundColor: element.color,position: "relative",fontWeight: "bold", color: "white",fontSize: "18px", border: "3px solid rgb(84,84,84)" }}>
<p>
<span>{element.room} </span>
</p>
<p>
<span
style={{ fontWeight: "bold", color: "rgb(84,84,84)" }}>{element.Name}
</span>
</p>
</button>
</Link>

try this
const deleteR = (i) => {
setRoomFilter((prevState) => {
var temp = prevState.filter((element, index) => index == i); //consider using a unique id
return [temp[0].Name];
});
};
note: I would recommend you use some sort of unique identification to remove from a list, and not an index.

Related

How do I use a useSwipeable handler for multiple elements

I'm trying to add support for touch events on slider inputs in my React app. Taps and drags work fine with bare React. The only other touch event I need is onTouchEnd to determine when a user has finished dragging a slider and the new value is to be committed.
I'm trying to use react-swipeable. I don't know if my question is specific to Swipeable or more generally to React. I've created the suggested handler for the onTouchEnd event and it works perfectly, but for only one element. I am mapping through 20 or 30 sliders and find that only the LAST slider works properly; the other sliders do not fire the handler at all.
I've tried it with and without the refPassthrough enhancement. The problem may be my limited understanding of useRef.
In the code below, three horizontal divs are created. Touching on the last (blue) div logs the event; the others don't.
I've also provided this working code in CodeSandBox
Any help would be appreciated,
Bill
import { useRef } from "react";
import { useSwipeable } from "react-swipeable";
export default function App() {
const handlers = useSwipeable({
onTouchEndOrOnMouseUp: (e) => console.log("User Touched!", e)
});
const myRef = useRef();
const refPassthrough = (el) => {
handlers.ref(el);
myRef.current = el;
};
return (
<div>
<h1>Re-use Swipeable handlers </h1>
<div
{...handlers}
style={{ backgroundColor: "red", height: "50px" }}
></div>
<div
{...handlers}
ref={refPassthrough}
style={{ backgroundColor: "green", height: "50px" }}
></div>
<div
{...handlers}
style={{ backgroundColor: "blue", height: "50px" }}
></div>
</div>
);
}
This is AN answer. It doesn't work perfectly for me because my use is within a function, not a functional component. I will try to re-work my app so that I can call useSwipeable only within functional Components.
import { useSwipeable } from "react-swipeable";
export default function App() {
return (
<div>
<h1>Re-use Swipeable handlers </h1>
<div
{...useSwipeable({
onTouchEndOrOnMouseUp: () => console.log("touch red")
})}
style={{ backgroundColor: "red", height: "50px" }}
></div>
<div
{...useSwipeable({
onTouchEndOrOnMouseUp: () => console.log("touch green")
})}
style={{ backgroundColor: "green", height: "50px" }}
></div>
<div
{...useSwipeable({
onTouchEndOrOnMouseUp: () => console.log("touch blue")
})}
style={{ backgroundColor: "blue", height: "50px" }}
></div>
</div>
);
}
Here is a more complete answer. Also in CodeSandbox
// It's surprisingly hard to process an onTouchEnd event for a slider in React.
// The useSwipeable hook does the heavy lifting.
// However, because it is a hook, it can't be .map-ped unless it is
// wrapped in a component.
// And, once it is wrapped in a component, it is hard to communicate
// onChange events to a parent component (the ususal tricks of passing
// setState or other changehandler do not seem to work for continuous
// slider onChange events.
// The approach here is to handle all of the onChange stuff in the wrapped
// component, including a local feedback display.
// Then, on the onTouchEnd event, the "normal" communication of the final
// value is returned to the parent via the dispatch prop.
import { useReducer, useState } from "react";
import { useSwipeable } from "react-swipeable";
export default function App() {
const [reduceState, dispatch] = useReducer(reducer, {
name1: "33",
name2: "66"
});
function reducer(state, action) {
return { ...state, [action.type]: action.data };
}
const MapWrappedSlider = (props) => {
const [currentValue, setCurrentValue] = useState(props.initialValue);
return (
<div style={{ backgroundColor: "cornsilk" }}>
<h2>{currentValue}</h2>
<input
type="range"
value={currentValue}
{...{
onChange: (e) => setCurrentValue(e.target.value),
onMouseUp: () =>
dispatch({ type: props.paramName, data: currentValue })
}}
{...useSwipeable({
// note: the current version of useSwipeable does not actually
// handle onMouseUp here. Also, the advertised onTouchEnd
// does not actually handle onTouchEnd
onTouchEndOrOnMouseUp: () =>
dispatch({ type: props.paramName, data: currentValue })
})}
/>
</div>
);
};
return (
<div style={{ textAlign: "center" }}>
<h1>SWIPEABLE MAPPED SLIDERS</h1>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-around"
}}
>
<h2>{reduceState.valueName}</h2>
{["name1", "name2"].map((paramName) => {
return (
<div key={paramName}>
<h1>{reduceState[paramName]}</h1>
<MapWrappedSlider
paramName={paramName}
initialValue={reduceState[paramName]}
dispatch={dispatch}
/>
</div>
);
})}
</div>
</div>
);
}

React, how can i change the button colors separately in every buttons?

i would like to change the button color, but i cant do it separately, i cant to use the color switch in every button one by one!
here is my code:
const [colorSwitch, setColorSwitch] = useState(true);
const slots = ['Slot 1', 'Slot 2'];
function SlotButton({ type, colorSwitch }: { type: string; colorSwitch: boolean }) {
return (
<Button
variant={colorSwitch ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
onClick={() => {
setColorSwitch(!colorSwitch);
}}>
{type}
</Button>
);
}
return (
<MainSection>
<DrawerHeader />
<div>
<h1 style={{ color: 'white' }}>Dryer Module</h1>
</div>
<CardBox>
{slots.map((item) => {
return <SlotButton key={item} type={item} colorSwitch={colorSwitch}></SlotButton>;
})}
</CardBox>
</MainSection>
);
} ```
You're currently using the same state variable (colorSwitch) for both buttons, thus they will always have the same state/color. As soon as one button is clicked, the state/variable is updated and both buttons are being re-rendered with the same updated state/variable.
You need to create separate state variables if you want them to behave differently. In a simple component like this I would use a state object that contains the different variables/values. This is one way you could do that:
// object with properties instead of one variable
const [colorSwitchObj, setColorSwitchObj] = useState({
"Slot 1": true,
"Slot 2": true,
});
const slots = ['Slot 1', 'Slot 2'];
function SlotButton({ type, colorSwitch }: { type: string; colorSwitchObj: boolean }) {
return (
<Button
{ /* colorSwitchObj[type] accesses matching property */}
variant={colorSwitchObj[type] ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
{ /* update by first copying the old state and then updating the specific value */}
onClick={() => {
setColorSwitchObj(...colorSwitchObj,
colorSwitchObj[type] = !colorSwitchObj[type]);
}}>
{type}
</Button>
);
}
const Main = () => {
//...
return (
<MainSection>
<DrawerHeader />
<div>
<h1 style={{ color: 'white' }}>Dryer Module</h1>
</div>
<CardBox>
{slots.map((item) => {
return <SlotButton key={item} type={item} colorSwitchObj={colorSwitchObj}></SlotButton>;
})}
</CardBox>
</MainSection>
);
}
When your components are simple like this it's usually fine to do it with a single useState object. However, once you start working with larger objects in your state, it makes sense to look into & learn the useReducer hook. Although this hook is slightly more complex, and takes a bit more effort to create, it can prevent many annoying state bugs because you get a much better organized way of maintaining your state.
There is little need for mapping here. It would be far easier to group both buttons together in their own Component:
function SlotButtons(isSwitched) {
return (
<Button
variant={colorSwitch ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
onClick={() => {
setColorSwitch(!colorSwitch);
}}>
{type}
</Button>
<Button
variant={!colorSwitch ? 'contained' : 'outlined'}
style={{ width: 240, height: 100, borderRadius: 20, margin: 15 }}
onClick={() => {
setColorSwitch(!colorSwitch);
}}>
{type}
</Button>
)
}
The second button reacts to inverted value of the first one. Or you could send both states to that component/

Filtering array to remove filtered object in react

Objective is to have an array with captured pokemons if user clicks on the input and an array of not-captured pokemons if user un-clicks the input. I've managed to filter out the pokemon when it's no longer captured and have it in the not-captured array but I can't seem to delete that pokemon from the old captured array.
Eg. If I click on "bulbasaur", "charmender", "squirtle", I get them all in the captured array. If I then remove one of them, I correctly get the removed one in the not-captured array but I can't seem to delete it from the previous captured array.
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import PokemonIcon from "./PokemonIcon";
const PokemonCard = ({ pokemon, capturedPkm, setCapturedPkm, notCapturedPkm, setNotCapturedPkm }) => {
const [label, setLabel] = useState('Not captured')
const toggleCaptured = (checked, id) => {
const pokemonName = { id: pokemon.id, name: pokemon.name }
if (checked && id === pokemon.id) {
setCapturedPkm([...capturedPkm, pokemonName])
setLabel('Captured!')
} else {
setLabel('Not captured!')
setNotCapturedPkm([...notCapturedPkm, pokemonName])
if (checked === false) {
let newArr = [...capturedPkm]
let pkmRemoved = newArr.filter((el, i) => el.id === id)
let newArrPkm = newArr.splice(pkmRemoved, 1)
console.log('newArr',newArrPkm, 'pkmRemoved', pkmRemoved)
setCapturedPkm(newArrPkm)
}
}
}
useEffect(() => {
console.log('captured', capturedPkm, 'not captured', notCapturedPkm)
}, [capturedPkm, notCapturedPkm])
return (
<>
<div
className="pokemon-card"
style={{
height: "250px",
maxWidth: "250px",
margin: "1rem",
boxShadow: "5px 5px 5px 4px rgba(0, 0, 0, 0.3)",
cursor: "pointer",
}}
>
<Link
to={{ pathname: `/pokemon/${pokemon.id}` }}
style={{ textDecoration: "none", color: "#000000" }}
state={{ pokemon: pokemon, capturedPkm }}
>
<div
style={{
padding: "20px",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<PokemonIcon img={pokemon.sprites?.['front_default']} />
</div>
</Link>
<div style={{ textAlign: "center" }}>
<h1>{pokemon.name}</h1>
<label>
<input type="checkbox"
defaultChecked={false}
value={pokemon.name}
onChange={(e) => toggleCaptured(e.target.checked, pokemon.id)} />
<span style={{ marginLeft: 8, cursor: 'pointer' }}>
{label}
</span>
</label>
</div>
</div>
<div></div>
</>
);
};
export default PokemonCard;
I guess you forgot to update the notCapturedPkm array. You could try something like this :
if (checked && id === pokemon.id) {
setCapturedPkm([...capturedPkm, pokemonName])
// Update this array, by removing the selected pokemon
setNotCapturedPkm([...notCapturedPkm.filter(pkm => pkm.id !== pokemon.id)])
setLabel('Captured!')
}

React Hooks useRef initialization issue, useRef only works on subsequent calls

I am implementing useRef into my project. I have a form that has clickable sections. Once clicked it opens the form. I'm using Reactstrap Collapse to show/hide the form. I need to be able to open the form and show the section that needs to be filled out, however the scrollIntoView once I click the section doesn't work until I open and close the form again. I'm stumped. I console.log(formRef), the ref returns as expected of the component that I want to be scrolled to the top of viewport on subsequent calls. My guess would be that the formRef is being initialized as null to begin with so initial calls to the ref do not work. However, once it knows the ref the subsequent calls work. I'm not sure how to go about this..
If I need to provide an example that is stripped please let me know. I am expecting this to be just an initialization issue.
Form
import React, { useRef, useContext, useEffect } from "react";
import {
FormQuestionsContext,
FormAnswersContext,
ExpandedSectionContext,
} from "../../Store";
import SectionHeader from "../SectionHeader";
import ImageUploader from "../CommentsSection";
import Ratings from "../Ratings";
import { Collapse, Button, CardBody, Card } from "reactstrap";
import FontAwesome from "react-fontawesome";
import styles from "./bedthreeform.module.css";
function BedThreeForm({ Name }) {
const formRef = useRef(null); //useRef Initialization
const [expandedSection, setExpandedSection] = useContext(
ExpandedSectionContext
);
const [formQuestions, setFormQuestions] = useContext(FormQuestionsContext);
const [formAnswers, setFormAnswers] = useContext(FormAnswersContext);
const array = formQuestions.bedthree;
const onChange = (e, name) => {
const { value } = e.target;
setFormAnswers((state) => ({
...state,
[Name]: { ...state[Name], [name]: value },
}));
};
//! The function I use when I want to tell useRef to scrollIntoView
const handleOpen = () => {
expandedSection === Name
? setExpandedSection("")
: setExpandedSection(Name);
formRef.current.scrollIntoView();
};
const answeredQuestions = formAnswers.bedthree
? Object.keys(formAnswers.bedthree)
: null;
console.log(formRef);
return (
<div>
<Button
className={styles["CollapseBtn"]}
onClick={handleOpen} //Calling the function here
style={
answeredQuestions &&
answeredQuestions.length === formQuestions.bedthree.length
? {
color: "white",
":focus": {
backgroundColor: "#02BD43",
},
backgroundColor: "#02BD43",
marginBottom: "1rem",
width: "100%",
}
: answeredQuestions &&
answeredQuestions.length !== formQuestions.bedthree.length
? {
color: "white",
":focus": {
backgroundColor: "#bd0202",
},
backgroundColor: "#bd0202",
marginBottom: "1rem",
width: "100%",
}
: {
":focus": {
backgroundColor: "#fafafa",
},
marginBottom: "1rem",
width: "100%",
}
}
>
<p>BEDROOM #3 INSPECTION</p>
<FontAwesome
className="super-crazy-colors"
name="angle-up"
rotate={expandedSection === Name ? null : 180}
size="lg"
style={{
marginTop: "5px",
textShadow: "0 1px 0 rgba(0, 0, 0, 0.1)",
}}
/>
</Button>
<Collapse
className={styles["Collapse"]}
isOpen={expandedSection === Name}
>
<Card>
<CardBody>
{array ? (
<div>
<SectionHeader title="Bedroom #3 Inspection" name={Name} />
<div
ref={formRef}
className={styles["BedroomThreeFormWrapper"]}
id="bedroom-three-form"
>
{array.map((question, index) => {
const selected =
formAnswers[Name] && formAnswers[Name][question]
? formAnswers[Name][question]
: "";
return (
<div className={styles["CheckboxWrapper"]} key={index}>
<h5>{question}</h5>
<Ratings
section={Name}
question={question}
onChange={onChange}
selected={selected}
/>
</div>
);
})}
</div>
{!answeredQuestions ? (
""
) : (
<Button
onClick={(e) => e.preventDefault()}
style={
!answeredQuestions ||
(answeredQuestions &&
answeredQuestions.length !==
formQuestions.bedthree.length)
? {
backgroundColor: "#bd0202",
color: "white",
pointerEvents: "none",
}
: {
backgroundColor: "#02BD43",
color: "white",
pointerEvents: "none",
}
}
>
{!answeredQuestions ||
(answeredQuestions &&
answeredQuestions.length !==
formQuestions.bedthree.length)
? "Incomplete"
: "Complete"}
</Button>
)}
<br />
<ImageUploader name="bedthree" title={"Bedroom #3"} />
</div>
) : (
<div></div>
)}
</CardBody>
</Card>
</Collapse>
</div>
);
}
export default BedThreeForm;
CodeSandbox Stripped Form Doesn't work as expected, however that is the stripped code.
Update I'm open to suggestions to bypass this, or an alternative way to do this. I'm not sure why it only does it on subsequent calls.
Look at these lines:
<CardBody>
{array ? (
...
<div
ref={formRef}
...
This (virtual) dom will be evaluated only if array is defined. In case you would like to have your formRef always to point to the dom, then You'll have to strip it out from your condition.
I've figured out the issue, the issue is calling it when the content in the collapse hasn't been loaded yet, Reactstrap has an attribute onEntered which basically when set, will run the function as soon as the collapse has fully opened. The example that I found is here. Also, by setting the attribute innerRef on a Reactstrap component I can manipulate it just like I could a regular component using ref.

Linking to a different page using react-day-picker and react router v4

I'm trying to utilize this example in order to create a calendar that lists out the events in the current month, I have this part working, but what I have yet to figure out is how to make it so that the user can click the event name and it would take them to that event page.
So per that example, if they click on one of the birthdays, it would take them to an events page where they could see more about that birthday.
Currently, my events page is being rendered using this function:
renderEvents() {
const {events} = this.state
this.state.events = {};
let eventItems = this.state.eventGet.map(event => {
console.log(event.id)
if(typeof(events[moment(event.date).date()]) !== "undefined") {
events[moment(event.date).date()].push(event.name)
} else {
events[moment(event.date).date()] = [event.name]
}
});
function renderDay(day) {
const date = day.getDate();
const dateStyle = {
position: 'absolute',
color: 'lightgray',
bottom: 0,
right: 0,
fontSize: 20,
};
const containerStyle = {
margin:'2px',
border: '1px solid #3a87ad',
borderRadius: '3px',
position: 'relative',
display: 'block',
cursor: 'pointer'
};
const textStyle = {
fontSize: '0.8em',
textAlign: 'left',
margin: '1.5px',
}
const cellStyle = {
height: 150,
width: 160,
position: 'relative',
};
return (
<div style={cellStyle}>
<div style={dateStyle}>{date}</div>
{events[date] &&
events[date].map((name, i) => (
<div onClick={() => this.props.history.push('/organizations/' + this.props.match.params.orgID + '/events' + i)} key={i} style={containerStyle}>
<div style={textStyle}> {name} </div>
</div>
))}
</div>
);
}
return (
<div>
<Grid component="section" className="section--center" shadow={0} noSpacing>
<Cell col={12}>
<FABButton style={{margin: '10px', float: "right"}} colored ripple onClick={() => this.props.history.push('/organizations/' + this.props.match.params.orgID + '/events')}>
<Icon name="add" />
</FABButton>
</Cell>
<DayPicker
canChangeMonth={true}
className="Birthdays"
renderDay={renderDay}
/>
</Grid>
</div>
);
}
The current problem is within the sub-function, renderDay which is called by the DayPicker component that gets the events associated with the day. When I try to push to the history property, it errors out and says that I cannot read property 'history' from undefined, which makes sense because we did not pass the history property to that function.
Can someone help me in figuring out how to modify that sub-function so that the onClick event on the div will take a user to that events page?
and says that I cannot read property 'history' from undefined
Make sure your renderDay function is bound to the correct this:
<DayPicker
canChangeMonth
className="Birthdays"
renderDay={renderDay.bind(this)}
/>

Resources