Mouseover in map loop reactjs - javascript

How to set hover for itself within map in Reactjs
hoverOn = () => {
this.setState({ hover: true});
}
hoverOff = () => {
this.setState({ hover: false });
}
...
var components = posts.map((post, i) => (
.....
<span className={this.state.hover ? 'showtooltip':'hidetooltip'} onHover={this.hoverOn} onMouseOut={this.hoverOff} onClick={(e) => this.viewPost(post, e)}><i className="quickview-button"></i></span>
......
));
Since I use setState, everytime I hover on span it reflects to all list data which returned from map loop.
How can I use hover to reflect on itself element?
Thank so much

wrap this logic in a little Component:
class Foo extends React.Component {
constructor(){
this.state = { hover: false };
}
render(){
return <span
{ ...this.props }
className={ this.state.hover ? 'showtooltip':'hidetooltip' }
onHover={ () => this.setState({ hover: true }) }
onMouseOut={ () => this.setState({ hover: false }) }
/>;
}
}
and use that Component:
...
var components = posts.map((post, i) => (
.....
<Foo onClick={(e) => this.viewPost(post, e)}><i className="quickview-button"></i></Foo>
......
));

You can make hover inside state an object state = { hover: {} } and set it with index i on events i.e. this.setState({ hover: {...this.state.hover, i: true }});.
Then pass i parameter into those methodsonHover={(i) => this.hoverOn(i)}
And change className setup to <span className={this.state.hover[i] ? 'showtooltip':'hidetooltip'} (thanks to #kenny for noticing this)

Related

Toggle specific item but not others when using map() and onClick() in React

I've created a project where I call an API to list all of the countries in the world and some facts about them. I have each country on a different card, and I want the card to say the country's name on the front and then flip to the back and show the country's continent and language when the user clicks the card. The problem is that when the user clicks the card, all of the cards flip. I realize now that I need to use id or something similar to target specific cards, but I can't figure out how.
What I've tried: There are various versions of this question on here and elsewhere, but often the code is much longer and it's harder to follow the advice, and some of those deal with changing the CSS or situations different from what I'm trying to do. I tried to create a state of 'clickedArray' to create an array that shows true of false for if any specific index is clicked, but I couldn't figure out where I could call a method that fills that array, and also I don't know if that's the correct strategy anyway.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: true,
clicked: false
}
}
componentDidMount() {
fetch('https://restcountries.eu/rest/v2/all')
.then(response => response.json())
.then(json => this.setState({data: json}));
this.setState({loading: false});
}
clickHappens = () => {
this.setState({clicked: this.state.clicked ? false : true});
}
render() {
return (
<div className="container">
{this.state.data?.length > 0 && this.state.data.map((item, id) => (
<div className="box" key={id} onClick={this.clickHappens}>
{this.state.clicked === false ?
<Countryname name={item["name"]}/>
:
<Countryinformation continent={item["subregion"]} language={item["languages"][0]["name"]} />
}
</div>
))}
</div>
)
}
}
class Countryname extends React.Component {
render() {
return (
<h1>{this.props.name}</h1>
)
}
}
class Countryinformation extends React.Component {
render() {
return (
<>
<p>{this.props.continent}</p>
<p>{this.props.language}</p>
</>
)
}
}
export default App;
This is because you are using a single state value to queue all the elements from if they are clicked or not.
If you want to toggle a single card at a time, use the index to match by.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: true,
clicked: null // <-- start with null state
}
}
componentDidMount() {
fetch('https://restcountries.eu/rest/v2/all')
.then(response => response.json())
.then(data => this.setState({ data }))
.finally(() => this.setState({ loading: false }));
}
clickHappens = (id) => () => {
this.setState(prevState => ({
clicked: prevState.clicked === id ? null : id, // <-- toggle back to null or to new id
}));
}
render() {
return (
<div className="container">
{this.state.data?.map((item, id) => (
<div
className="box"
key={id}
onClick={this.clickHappens(id)} // <-- pass id to toggle
>
{this.state.clicked === id ? // <-- check id match
<Countryinformation
continent={item["subregion"]}
language={item["languages"][0]["name"]}
/>
:
<Countryname name={item["name"]}/>
}
</div>
))}
</div>
)
}
}
If you want to toggle multiple then use a map object to store the ids.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
loading: true,
clicked: {} // <-- start with empty object
}
}
componentDidMount() {
fetch('https://restcountries.eu/rest/v2/all')
.then(response => response.json())
.then(data => this.setState({ data }))
.finally(() => this.setState({ loading: false }));
}
clickHappens = (id) => () => {
this.setState(prevState => ({
clicked: {
...prevState.clicked,
[id]: !prevState.clicked[id], // <-- toggle clicked boolean
},
}));
}
render() {
return (
<div className="container">
{this.state.data?.map((item, id) => (
<div
className="box"
key={id}
onClick={this.clickHappens(id)} // <-- pass id to toggle
>
{this.state.clicked[id] ? // <-- check id match
<Countryinformation
continent={item["subregion"]}
language={item["languages"][0]["name"]}
/>
:
<Countryname name={item["name"]}/>
}
</div>
))}
</div>
)
}
}

How to fix onClick element. ReactJS

I new in ReactJS and i have one few question. I defined function showModal and but console.log() and
this.setState({show:!this.state.show});.
And after that I applied
this function onClick event for div element inside map function.
1st question: When I click on div element showModal work but in console I don't see my console.log.
2nd question: I want to make when you click on one div element it must add/show few new div elements but only for one div element (on which I clicked). But now when I click on one div element it add/show new elements for all div elements which had this showModal function.
How can i fix this
import React, { Component } from "react";
import Modal from '../components/modal/form'
const DEFAULT_QUERY = 'redux';
const PATH_BASE = 'URL which work correct';
class Actions extends React.PureComponent{
constructor(){
super();
this.state = {
result:null,
show:false,
helpId:null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.showModal = this.showModal.bind(this);
this.handleClickFromParent = this.handleClickFromParent.bind(this);
this.onClose = this.onClose.bind(this);
}
onClose = e => {
this.setState({ show: false});
}
handleClickFromParent = e => {
this.setState({show: !this.state.show});
}
showModal = e => {
console.log('BABE');
this.setState({show: !this.state.show})
};
setSearchTopStories(result) {
this.setState({ result });
};
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
};
render(){
const { searchTerm, result } = this.state;
console.log('* Actions Pure*');
console.log(result);
console.log('=');
return(
<div>
{
(result !== null) ?
result.map(
(item,index) =>
<div>
<div onClick={()=>this.showModal()}>{item.name}</div>
<Modal
id = {index}
handleClickFromParent {this.handleClickFromParent}
item = {[item]}
show = {this.state.show}
onClose = {this.onClose}>
YOLO
</Modal>
</div>
)
: null
}
</div>
)
}
}
export default Actions;
While selecting u can pass the item on method, and on click u can set the item value. Please check the below code.
Demo:
https://codesandbox.io/s/stackoverflowmodal-19i36
this.state = {
result: null,
show: false,
selectedItem:null,
helpId: null
};
//
showModal = (selectedItem) => {
this.setState({
show: !this.state.show,
selectedItem
});
};
//
class Actions extends React.PureComponent {
constructor() {
super();
this.state = {
result: null,
show: false,
selectedItem: null,
helpId: null
};
this.setSearchTopStories = this.setSearchTopStories.bind(this);
this.showModal = this.showModal.bind(this);
this.handleClickFromParent = this.handleClickFromParent.bind(this);
this.onClose = this.onClose.bind(this);
}
onClose = e => {
this.setState({
show: false
});
};
handleClickFromParent = e => {
this.setState({
show: !this.state.show
});
};
showModal = selectedItem => {
this.setState({
show: !this.state.show,
selectedItem
});
};
setSearchTopStories(result) {
this.setState({ result });
}
componentDidMount() {
fetch(`${PATH_BASE}`)
.then(response => response.json())
.then(result => this.setSearchTopStories(result))
.catch(error => error);
}
render() {
const { searchTerm, result, selectedItem } = this.state;
return (
<div>
{result && result.length
? result.map((item, index) => (
<div>
<div onClick={() => this.showModal(item)}>{item.name}</div>
</div>
))
: null}
{selectedItem && (
<Modal
id={index}
handleClickFromParent={this.handleClickFromParent}
item={[selectedItem]}
show={this.state.show}
onClose={this.onClose}
>
YOLO
</Modal>
)}
</div>
);
}
}
export default Actions;

How can I display a modal after a delay?

I'm trying to load a modal 2 seconds after the page has been loaded. I tried setting the state on componentDidUpdate but I keep on getting active: undefined The active props determines the visibility of the modal on the page. I tried toggling it to true on browser on the react tool and my modal shows up. I'm just not sure how to load to it 2 seconds after the page loads up.
state = { show: true };
showModal = () => {
this.setState({ show: true });
};
closeModal = () => {
this.setState({ show: false });
};
render() {
const { showModal } = this.state;
return (
<React.Fragment>
....
<Modal.ModalAnimator active={showModal} onClose={this.closeModal}>
<Modal.ModalWithCross
onClose={this.closeModal}
>
<h3>Are you interested in any other Additions?</h3>
<Section>
<p>Hit “notify concierge” and we’ll be in touch shortly.</p>
</Section>
</Modal.ModalWithCross>
</Modal.ModalAnimator>
</React.Fragment>
)
}
When destructuring the state, you write showModal instead of the actual state field name show. So your first lines in the render function should read:
render() {
const { show } = this.state;
return (
<React.Fragment>
...
<Modal.ModalAnimator active={show} onClose={this.closeModal}>
...
Please try this.
state = { show: true };
closeModal = () => {
this.setState({ show: false });
};
componentDidMount() {
setTimeout(() => {
this.setState({ show: true });
}, 2000);
}
render() {
const { showModal } = this.state;
return (
let model = null;
if (this.state.show) {
let model = (
<Modal.ModalAnimator active={showModal} onClose={this.closeModal}>
<Modal.ModalWithCross
onClose={this.closeModal}
>
<h3>Are you interested in any other Additions?</h3>
<Section>
<p>Hit “notify concierge” and we’ll be in touch shortly.</p>
</Section>
</Modal.ModalWithCross>
</Modal.ModalAnimator>
)
}
<React.Fragment>
....
{model}
</React.Fragment>
)
}

React Compound Components type warning

I have a simple compound component with a bunch of static subcomponents:
// #flow
import React, { Component, Children } from 'react';
type Props = {
children: React.ChildrenArray<React.Node> | React.Node,
}
class Toggle extends Component<Props> {
static On = props => (props.on ? props.children : null);
static Off = props => (props.on ? null : props.children);
static Button = props => (
<button
onClick={props.toggle}
type="button"
style={{ display: 'inline-block' }}
>
<pre>{JSON.stringify(props.on, null, 2)}</pre>
</button>
);
state = { on: false }
toggle = () => {
this.setState(
({ on }) => ({ on: !on }),
// maybe this.props.someCallback
() => console.log(this.state.on),
);
}
render() {
return Children.map(
this.props.children,
childElem => React.cloneElement(childElem, {
on: this.state.on,
toggle: this.toggle,
}),
);
}
}
export default Toggle;
The warning happens when I try to put some other elements into Toggle children scope.
For example:
<Toggle>
<Toggle.On>On</Toggle.On>
<span /> <-- this is savage
<Toggle.Button />
<Toggle.Off>Off</Toggle.Off>
</Toggle>
Everything is working, but my flowtype warn me about this span like so:
Warning: Received `false` for a non-boolean attribute `on`.....
Warning: Invalid value for prop `toggle` on <span> tag....
How can I to pacify this nasty girl?
Thank you guys, I think, right solution is just check if type of mounted node is correct one, otherwise - just clone node with regular node props:
// #flow
import React, { Component, Children } from 'react';
type Props = {
children: React.ChildrenArray<React.Node> | React.Node,
}
class Toggle extends Component<Props> {
static On = props => (props.on ? props.children : null);
static Off = props => (props.on ? null : props.children);
static Button = props => (
<button
onClick={props.toggle}
type="button"
style={{ display: 'inline-block' }}
>
<pre>{JSON.stringify(props.on, null, 2)}</pre>
</button>
);
state = { on: false }
toggle = () => {
this.setState(
({ on }) => ({ on: !on }),
// maybe this.props.someCallback
() => console.log(this.state.on),
);
}
// Checking here
allowedTypes = ({ type }) => {
return [
(<Toggle.On />).type,
(<Toggle.Off />).type,
(<Toggle.Button />).type,
].includes(type);
}
render() {
return Children.map(
this.props.children,
(childElem) => {
const elemProps = this.allowedTypes(childElem) ? {
on: this.state.on,
toggle: this.toggle,
} : childElem.props;
return React.cloneElement(childElem, elemProps);
},
);
}
}
export default Toggle;
You can also do this, just having the components in a list and checking their type inside .map, putting on the custom props or otherwise just returning the original child.
const allowedTypes = [ToggleOn, ToggleOff, ToggleButton]
return React.Children.map(props.children, child => {
if (allowedTypes.includes(child.type)) {
return React.cloneElement(child, {on, toggle})
}
return child
})
}

Change Specific Property of the State

I am learning ReactJS and needless to say I am an absolute beginner! I am trying to change a specific property in the array of objects which belongs to state. Every object has two properties: name and active. active values are false by default. When I click on the item, I want to change this item's active value to true.
My array is shown inside of the list element and every list element has onClick={() => props.onChangeSelected(lang.name)} method. onChangeSleceted method goes to handleChangeSelected(name) function, however, I couldn't figure out what to write inside of this function.
class Loading extends React.Component {
constructor(props) {
super(props);
this.state = {
text: 'Loading'
};
}
componentDidMount() {
const stopper = this.state.text + '...';
this.interval = window.setInterval(() => {
this.state.text === stopper
? this.setState(() => ({ text: 'Loading' }))
: this.setState((prevState) => ({ text: prevState.text + '.' }))
}, 300)
}
componentWillUnmount() {
window.clearInterval(this.interval);
}
render() {
return (
<p>
{this.state.text}
</p>
)
}
}
function LanguageList (props) {
return (
<div>
<h3>Choose your favorite:</h3>
<ul>
{props.list.map((lang) => (
<li key={lang.name} onClick={() => props.onChangeSelected(lang.name)}>
<span>{lang.name}</span>
</li>
))}
</ul>
</div>
)
}
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
languages: [
{
name: 'all',
active: true
},
{
name: 'javascript',
active: false
},
{
name: 'ruby',
active: false
},
{
name: 'python',
active: false
}
]
}
this.handleChangeSelected = this.handleChangeSelected.bind(this)
}
handleChangeSelected(name) {
this.setState((currentState) => {
const lang = currentState.languages.find((lang) => lang.name === name)
return {}
})
}
render() {
return (
<div>
<LanguageList
list={this.state.languages}
onChangeSelected={this.handleChangeSelected}
/>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('app')
)
</script>
You can do it in a number of ways. All you need to make sure is that you aren't mutating the original state array
handleChangeSelected(name) {
this.setState((currentState) => {
return { languages: currentState.languages.map((lang) => {
if(lang.name === name) {
return {...lang, active: true};
}
return lang;
});
})
}
Try this?
handleChangeSelected(name){
// Find matching element in state
var temp = this.state.languages;
for (var i = 0; i < temp.length; i++){
if (temp[i]["name"] === name){
temp[i]["active"] = true;
}
}
this.setState({
languages: temp
});
}
As listed in the React docs, they recommend creating a new object when calling the setState function. This is of course talking about the updater function syntax (this.setState((prevState, props) => {return {...};});), which I assume the same logic is applied to the syntax used above (passing an object into set state)
The first argument [to setState] is an updater function with the signature:
(prevState, props) => stateChange
(prevState, props) => stateChange prevState is a reference to the
previous state. It should not be directly mutated. Instead, changes
should be represented by building a new object based on the input from
prevState and props.

Categories