reactjs ref only works for last element in array - arrays

I am having a parent component and a child component. I want to call a function from my child in my parent component. I am using ref and it works fine, but just for my last element in my array...
Parent:
onClick = () => {
this.child.handleClose(); // do stuff
};
var items = [];
tracks.filter(searchingFor(term)).map(function (title, i) {
items.push(
<div>
<ItemViewAll
onRef={(ref) => (this.child = ref)}
triggerOpen={this.handleOpenParent.bind(this)}
triggerClose={this.handleCloseParent.bind(this)}
key={title.id}
title={title.title}
/>
</div>
);
}, this);
ItemViewAll is imported from my Child component, where the needed functions are located.
Child:
componentDidMount() {
this.props.onRef(this)
}
componentWillUnmount() {
this.props.onRef(undefined)
}
handleClose = () => {
this.setState({ open: false });
this.props.triggerClose();
}
It seems like that the ref works only for the last element in my items[]...
Do you have any suggestions on how to make it work for every element in my array?
Here is my full Parent Compoenent, could be heldpful. Still have no clue how to set the ref...
Parent (complete):
const { scaleDown } = transitions;
function searchingFor(term) {
return function (x) {
return (
x.title.toLowerCase().includes(term.toLowerCase()) ||
x.body.toLowerCase().includes(term.toLowerCase())
);
};
}
class ViewAll extends React.Component {
constructor(props) {
super(props);
this.state = {
term: "",
mounted: false,
tracks: [],
hasMoreItems: true,
};
this.searchHandler = this.searchHandler.bind(this);
this.focus = this.focus.bind(this);
}
loadContent() {
var requestUrl = this.props.url;
fetch(requestUrl)
.then((response) => {
return response.json();
})
.then((tracks) => {
this.setState({ tracks: this.state.tracks.concat(tracks) });
})
.catch((err) => {
console.log("There has been an error");
});
}
componentDidMount() {
var requestUrl = this.props.url;
fetch(requestUrl)
.then((response) => {
return response.json();
})
.then((data) => {
this.setState({ tracks: data });
})
.catch((err) => {
console.log("There has been an error");
});
window.scrollTo(0, 0);
this.focus();
}
handleOpenParent() {
this.setState({ show: true });
}
handleCloseParent() {
this.setState({ show: false });
}
searchHandler(event) {
this.setState({ term: event.target.value });
}
focus() {
this.textInput.focus();
}
onClick = () => {
this.child.handleClose(); // do stuff
};
render() {
const { term, data, tracks } = this.state;
const loader = <div className="loader"></div>;
var items = [];
tracks.filter(searchingFor(term)).map(function (title, i) {
items.push(
<div>
<MuiThemeProvider>
<Paper
style={{
borderRadius: "2em",
background:
"linear-gradient(to right, #82f2da 30%, white 100%)",
}}
zDepth={1}
>
<ItemViewAll
onRef={(ref) => (this.child = ref)}
triggerOpen={this.handleOpenParent.bind(this)}
triggerClose={this.handleCloseParent.bind(this)}
key={title.id}
title={title.title}
score={title.vote_average}
overview={title.body}
backdrop={title.image}
description={title.description}
messenger={title.messenger}
twitter={title.twitter}
discord={title.discord}
slack={title.slack}
kik={title.kik}
telegram={title.telegram}
/>
</Paper>
</MuiThemeProvider>
</div>
);
}, this);
return (
<div>
<header className="Header">
{this.state.show && (
<div
style={{ height: 50, width: 50, background: "red" }}
onClick={this.onClick}
></div>
)}
</header>
<div>
<Media query="(max-width: 599px)">
{(matches) =>
matches ? (
<div style={{ marginTop: 100 }}>
<div
style={{
width: "90%",
marginLeft: "auto",
marginRight: "auto",
marginBottom: 50,
}}
>
<MuiThemeProvider>
<TextField
hintText="Welcher Bot darf es sein?"
type="Text"
onChange={this.searchHandler}
value={term}
fullWidth={true}
underlineFocusStyle={{
borderColor: "#82f2da",
borderWidth: 3,
}}
underlineStyle={{
borderColor: "#82f2da",
borderWidth: 1.5,
top: "40px",
}}
hintStyle={{ fontSize: 30, fontFamily: "Anton" }}
inputStyle={{ fontSize: 30, fontFamily: "Anton" }}
ref={(input) => {
this.textInput = input;
}}
style={{ caretColor: "#82f2da" }}
/>
</MuiThemeProvider>
</div>
</div>
) : (
<div style={{ marginTop: 130 }}>
<div
style={{
width: "80%",
marginLeft: "auto",
marginRight: "auto",
marginBottom: 70,
}}
>
<MuiThemeProvider>
<TextField
hintText="Welcher Bot darf es sein?"
type="Text"
onChange={this.searchHandler}
value={term}
fullWidth={true}
underlineFocusStyle={{
borderColor: "#82f2da",
borderWidth: 3,
}}
underlineStyle={{
borderColor: "#82f2da",
borderWidth: 1.5,
top: "50px",
}}
hintStyle={{ fontSize: 40, fontFamily: "Anton" }}
inputStyle={{ fontSize: 40, fontFamily: "Anton" }}
ref={(input) => {
this.textInput = input;
}}
style={{ caretColor: "#82f2da" }}
/>
</MuiThemeProvider>
</div>
</div>
)
}
</Media>
<Media query="(max-width: 599px)">
{(matches) =>
matches ? (
<InfiniteScroll
pageStart={1}
loadMore={this.loadContent.bind(this)}
hasMore={this.state.hasMoreItems}
loader={loader}
initialLoad={false}
>
<StackGrid
columnWidth={180}
gutterHeight={10}
gutterWidth={10}
duration={1500}
monitorImagesLoaded={true}
easing={easings.quadInOut}
appear={scaleDown.appear}
appeared={scaleDown.appeared}
enter={scaleDown.enter}
entered={scaleDown.entered}
leaved={scaleDown.leaved}
>
{items}
</StackGrid>
</InfiniteScroll>
) : (
<InfiniteScroll
pageStart={10}
loadMore={this.loadContent.bind(this)}
hasMore={this.state.hasMoreItems}
loader={loader}
initialLoad={true}
>
<StackGrid
columnWidth={180}
gutterHeight={80}
gutterWidth={80}
duration={1500}
monitorImagesLoaded={true}
easing={easings.quadInOut}
appear={scaleDown.appear}
appeared={scaleDown.appeared}
enter={scaleDown.enter}
entered={scaleDown.entered}
leaved={scaleDown.leaved}
>
{items}
</StackGrid>
</InfiniteScroll>
)
}
</Media>
</div>
</div>
);
}
}
export default ViewAll;
The onClick function schould be called when clicked on the div in my header. But for all elements in my array and not just for the last one.... The onClick function calls again the handleClose() in my Child.

I believe the reason you always get the last element instance, it is because you are iterating an array and for every item, you render a new and overwrite the this.child variable with a new reference of .
But even though, you should not create a ref for each item of element. If you want to get the onClick event, you have to implement this event on the component . If the component is from a third party library, check the documentation to see how to handle events. Usually, it is with onClick event.

hard to say for sure without seeing more of parent component code (i think you may have cut something from that accidentally), so it's just a guess, but you are probably overwriting your ref while mapping over array, hence it's working only for last element. you can find some solutions for that here.

Related

Fetch axios response to new window or new url

i'm created one dropdown that used for querying the database and show the result as axios response . but right now i can only use it in console.log(res.data) what i want is something like load response in new url or new file.
full react code:
import React from "react";
import axios from 'axios';
class Inference extends React.Component {
constructor(props) {
super(props);
this.state = {
courses: [],
course: "",
model: "",
options: [
{ label: "model1", value: "model1" },
{ label: "model2", value: "model2" },
{ label: "model3", value: "model3" },
],
};
this.handleChangeCourse = this.handleChangeCourse.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange = event => {
this.setState({ model: event.target.value });
};
handleChangeCourse = event => {
this.setState({ course: event.target.value });
};
getUnique(arr, comp) {
const unique = arr
//store the comparison values in array
.map(e => e[comp])
// store the keys of the unique objects
.map((e, i, final) => final.indexOf(e) === i && i)
// eliminate the dead keys & store unique objects
.filter(e => arr[e])
.map(e => arr[e]);
return unique;
}
handleSubmit(event) {
event.preventDefault();
axios.post('http://localhost:5000/getmodell', {
model: this.state.model,
dataset:this.state.course
})
.then((res) => {
// Res.data is the response from your server
console.log(res.data);
});
}
componentDidMount() {
axios.get('http://localhost:5000/files')
.then(response => this.setState({ courses: response.data }));
}
render() {
const uniqueCouse = this.getUnique(this.state.courses, "dataset");
const courses = this.state.courses;
const course = this.state.course;
const options = this.state.options;
const filterDropdown = courses.filter(function(result) {
return result.dataset === course;
});
return (
<div className="container">
<div className="row">
<form onSubmit={this.handleSubmit.bind(this)}>
<div className="col-4"
style={{
paddingBottom: "100px",
paddingTop: "20px",
alignItems: "center",
}}>
<label style={{ paddingTop: "5px", marginTop: "40px" }}>
Dataset
<select classname="custom-select"
value={this.state.course}
onChange={this.handleChangeCourse} style={{ paddingTop: "5px", marginTop: "10px" }}
>
{uniqueCouse.map(course => (
<option key={course.id} value={course.dataset}>
{course.dataset}
</option>
))}
</select>
</label>
<div className="col-4">
<button
type="submit"
class="btn"
style={{ marginTop: "" }}
>
ok
</button>
</div>
<div>
{filterDropdown.map(course => (
<div key={course.id} style={{ margin: "10px" }}>
<img src={`${course.path}`} height="80" className="card-img-top img-responsive" alt="img"/>
<br />
</div>
))}
</div>
</div>
<div
className="col-4"
style={{
paddingBottom: "100px",
paddingTop: "20px",
alignItems: "center",
}}
>
<label
className=""
style={{ paddingTop: "5px", marginTop: "40px" }}
>
Model
<select
className="custom-select"
name="example"
value={this.state.model}
onChange={this.handleChange}
style={{ paddingTop: "5px", marginTop: "10px" }}
>
<option>--Select--</option>
{options.map((option) => (
<option
value={option.value}
onChange={(e) => this.setState({ model: e.target.value })}
>
{option.label}
</option>
))}
</select>
</label>
</div>
</form>
</div>
</div>
);
}
}
export default Inference;
this code works perfectly fine i can able to see the results in console log. i want to see the results in new url or stores as new json. kindly help me

Maximum call stack size exceeded while integrating in react-id-swiper

I am Using rect-id-swiper for carousel in my project. below is my code.
class Slide extends Component {
constructor(props) {
super(props);
this.state = {
data: [],
load: false,
next: "",
error: null
};
}
componentDidMount() {
this.setState({ loading: true });
const token = getTokenu();
Axios.get(ApiUrlConstant.getApiFullUrl("slide"), {
headers: {
Authorization: "Token " + token
}
})
.then(res => {
console.log(res.data.results);
this.setState({ data: res.data.results, loding: false });
})
.catch(error => {
this.setState({ error: error, loading: false });
});
}
renderCompetitionSlides = () => {
if (this.state.data.length === 0)
return (
<div
style={{
width: "100%",
display: "flex",
justifyContent: "center",
color: "#f3990f"
}}
>
<span>No Poster to display!</span>
</div>
);
return this.state.data.map(item => {
return (
<div key={item.id}>
<Link className="card" to="">
<div className="swiperimgweb">
<img style={{ width: "100%" }} src={item.poster} />
</div>
</Link>
</div>
);
});
};
render() {
const { data, error, load, next } = this.state;
SwiperCore.use([Autoplay, Navigation]);
const swiperhero = {
spaceBetween: 30,
centeredSlides: true,
autoplay: {
delay: 5000,
disableOnInteraction: false
},
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev"
}
};
return (
<div>
<Swiper {...swiperhero}>{this.renderCompetitionSlides()}</Swiper>
{this.state.load ? (
<div
id="slide-load"
style={{
marginTop: "100px",
marginBottom: "30px",
width: "100%",
display: "flex",
justifyContent: "center"
}}
>
<ClipLoader size={30} color={"#f3990f"} />
</div>
) : null}
</div>
);
}
}
export default Slide;
And I am trying to render these API's
I think I have done in right way. but still am getting error on console and The images are not coming as I need. click here to check the react-id-swiper.
scheduler.development.js:178 Uncaught RangeError: Maximum call stack size exceeded
What is the mean of above error and How can we fix this?

Any React Tags Input Component where tags can be edited?

I have been looking everywhere but i didn't seem to find any component that allows a tag to be editable. currently i am using
https://github.com/JedWatson/react-select
and here is my code:
<CreatableSelect
styles={{
multiValueLabel: (provided, state) => ({
...provided,
whiteSpace: "normal"
})
}}
cropWithEllipsis={false}
components={components}
inputValue={inputValue}
isClearable={false}
isMulti
menuIsOpen={false}
onChange={this.handleChange}
onInputChange={this.handleInputChange}
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown}
value={value}
className={styles.questionInput}
onValueClick={this.onValueClick}
/>
but it doesn't have any props or method to edit a tag just delete. onValueClick does not do anything.
i found this:
https://goodies.pixabay.com/jquery/tag-editor/demo.html
but since i am new to react/typescript i dont know how to implement this to my React Typescript project.
Thank you for your help.
Nevermind looking for a component. i found this online, doesnt have an edit functionality but you can get the item onfocus so i tried modifying it.
https://codepen.io/srph/pen/WrPywK
class TagInput extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
focused: false,
input: ''
};
this.handleInputChange = this.handleInputChange.bind(this);
this.handleInputKeyDown = this.handleInputKeyDown.bind(this);
this.handleRemoveItem = this.handleRemoveItem.bind(this);
}
render() {
const styles = {
container: {
border: '1px solid #ddd',
padding: '5px',
borderRadius: '5px',
},
items: {
display: 'inline-block',
padding: '2px',
border: '1px solid blue',
fontFamily: 'Helvetica, sans-serif',
borderRadius: '5px',
marginRight: '5px',
cursor: 'pointer'
},
input: {
outline: 'none',
border: 'none',
fontSize: '14px',
fontFamily: 'Helvetica, sans-serif'
}
};
return (
<label>
<ul style={styles.container}>
{this.state.items.map((item, i) =>
<li key={i} style={styles.items} onClick={this.handleRemoveItem(i)}>
{item}
<span>(x)</span>
</li>
)}
<input
style={styles.input}
value={this.state.input}
onChange={this.handleInputChange}
onKeyDown={this.handleInputKeyDown} />
</ul>
</label>
);
}
handleInputChange(evt) {
this.setState({ input: evt.target.value });
}
handleInputKeyDown(evt) {
if ( evt.keyCode === 13 ) {
const {value} = evt.target;
this.setState(state => ({
items: [...state.items, value],
input: ''
}));
}
if ( this.state.items.length && evt.keyCode === 8 && !this.state.input.length ) {
this.setState(state => ({
items: state.items.slice(0, state.items.length - 1)
}));
}
}
handleRemoveItem(index) {
return () => {
this.setState(state => ({
items: state.items.filter((item, i) => i !== index)
}));
}
}
}
ReactDOM.render(
<TagInput />,
document.getElementById('app')
);
so recreated it to suit my needs:
https://codesandbox.io/s/n03r8w74m

How to delete a div with id slider using ReactJS?

I am trying to delete div section without using state, how to remove this? I tried by using unmountComponentAtNode but showing the error
unmountComponentAtNode(): The node you're attempting to unmount was rendered by React and is not a top-level container. Instead, have the parent component update its state and rerender in order to remove this component.
Code:
import 'rc-slider/assets/index.css';
import 'rc-tooltip/assets/bootstrap.css';
import React from 'react';
import Tooltip from 'rc-tooltip';
import Slider from 'rc-slider';
import { withStyles } from '#material-ui/core/styles';
import Icon from '#material-ui/core/Icon';
import { unmountComponentAtNode } from 'react-dom';
const createSliderWithTooltip = Slider.createSliderWithTooltip;
const Range = createSliderWithTooltip(Slider.Range);
const Handle = Slider.Handle;
const marks = {
0: <strong>0°C</strong>,
26: '26°C',
37: '37°C',
50: '50°C',
100: {
style: {
color: 'red',
},
label: <strong>100°C</strong>,
},
};
const handle = (props) => {
const { value, dragging, index, ...restProps } = props;
return (
<Tooltip
prefixCls="rc-slider-tooltip"
overlay={value}
visible={dragging}
placement="top"
key={index}
>
<Handle value={value} {...restProps} />
</Tooltip>
);
};
class StepSlider extends React.Component {
constructor(props) {
super(props);
this.state = { sliderValues: [80] };
this.onDelEvent = this.onDelEvent.bind(this)
}
handleChange = sliderValues => {
this.setState({ sliderValues });
};
onDelEvent = (e) => {
console(e)
var object = this.refs.slider;
unmountComponentAtNode(object);
document.body.removeChild(object);
}
render() {
const { classes } = this.props;
const { sliderValues } = this.state;
return (
<div className="row" style={{ margin: '50px' }} ref="slider" id="slider">
<div className="col-md-11">
<div className="box-header" style={{ textAlign: 'center', fontSize: '20px' }}><strong>Project NPV: $2.98M</strong></div>
<p style={{ position: 'absolute', color: 'pink' }}>kjh</p>
{/* <Slider min={0} max={100} marks={marks} defaultValue={sliderValues} onChange={this.handleChange} handle={handle} tipFormatter={value => `${value}%`}/> */}
<Slider
min={0} max={100} marks={marks} defaultValue={sliderValues} onChange={this.handleChange} handle={handle} tipFormatter={value => `${value}%`}
trackStyle={{ backgroundColor: 'blue', height: 15 }}
handleStyle={{
borderColor: 'red',
height: 35,
width: 35,
marginLeft: -14,
marginTop: -9,
backgroundColor: 'white',
}}
railStyle={{ backgroundColor: 'black', height: 15 }}
/>
</div>
<div className="col-md-1">
<Icon className={classes.icon} color="primary" onClick={this.onDelEvent.bind(this)} style={{ marginTop: '45px' }}>
remove_circle</Icon>
{/* <div style={wrapperStyle}>
<p>Range with custom handle</p>
<Range min={0} max={20} defaultValue={[3, 10]} tipFormatter={value => `${value}%`} />
</div> */}
</div>
</div>
)
}
}
export default withStyles({ withTheme: true })(StepSlider);
I suggest the following pattern for dynamic removal of sliders:
Slider.js
const Slider = ({ id, onRemove }) => (
<div className="slider">
<button onClick={() => onRemove(id)} />
</div>
);
export default Slider;
StepSlider.js
import Slider from './Slider';
class StepSlider extends React.Component {
state = {
sliders: [
{id: 1, value: ...},
{id: 2, value: ...},
...
]
}
handleRemoveSlider = id => {
this.setState(prevState => ({
sliders: [...prevState.sliders.filter(slider => slider.id !== id)]
}))
}
render() {
this.state.sliders.map(slider => (
<Slider id={slider.id} onRemove={this.handleRemoveSlider} />
))
}
}
import 'rc-slider/assets/index.css';
import 'rc-tooltip/assets/bootstrap.css';
import React from 'react';
import Tooltip from 'rc-tooltip';
import Slider from 'rc-slider';
import { withStyles } from '#material-ui/core/styles';
import Icon from '#material-ui/core/Icon';
const createSliderWithTooltip = Slider.createSliderWithTooltip;
const Range = createSliderWithTooltip(Slider.Range);
const Handle = Slider.Handle;
const marks = {
0: '5%',
10: '6%',
20: '7%',
30: '8%',
40: '9%',
50: '10%',
60: '11%',
70: '12%',
80: '13%',
90: '14%',
100: '15%'
};
const handle = (props) => {
const { value, dragging, index, ...restProps } = props;
return (
<Tooltip
prefixCls="rc-slider-tooltip"
overlay={value}
visible={dragging}
placement="top"
key={index}
>
<Handle value={value} {...restProps} />
</Tooltip>
);
};
const handleStyle = {
borderColor: 'red',
height: 28,
width: 28,
marginLeft: -14,
marginTop: -8,
backgroundColor: 'white',
}
class StepSlider extends React.Component {
constructor() {
super()
this.state = {
sliders: [
{ id: 1, title: 'Discount Rate', value: '5' },
{ id: 2, title: 'Total Volume', value: '10' },
{ id: 3, title: 'Avg Sales Rate', value: '50' }
]
}
this.handleRemoveSlider = this.handleRemoveSlider.bind(this);
this.onChange=this.onChange.bind(this)
}
onChange=(e)=>{
let inputValue = e;
let statusCopy = Object.assign({}, this.state);
statusCopy.value = inputValue;
this.setState(statusCopy);
}
handleRemoveSlider = id => {
this.setState(prevState => ({
sliders: [...prevState.sliders.filter(slider => slider.id !== id),
],
}))
}
render() {
return (
this.state.sliders.map(slider => (
<div>
<Button id={slider.id} onRemove={this.handleRemoveSlider} title={slider.title} value={slider.value} onChange={this.onChange}/>
</div>
))
)
}
}
export default StepSlider;
const Button = ({ id, onRemove, value, title,onChange }) => (
<div className="row" style={{ margin: '50px' }} id={id}>
<div className="col-md-1">
{title}
</div>
<div className="col-md-10">
<Slider min={0} max={100} handle={handle}
defaultValue={value} marks={marks}
tipFormatter={value => `${value}%`}
trackStyle={{ backgroundColor: 'blue', height: 15 }}
handleStyle={handleStyle}
railStyle={{ backgroundColor: 'black', height: 15 }}
onChange={onChange}
/>
</div>
<div className="col-md-1">
<button onClick={() => onRemove(id)} style={{ backgroundColor: '#1a237e', color: 'white' }}>X</button>
</div>
</div>
);
Actually this is working, i mean deleting slider by passing its respective id but state is not updating and i wanna update the particular slider value onchanging it. please check and help me

react-cropper : Uncaught TypeError: Cannot call a class as a function

i'm working on a system for uploading and cropping image with the react-cropper component.
I'm block when i want to create a component which contains the crop system and the preview.
This is my container :
class PhotoContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
cropResult:null
};
this.changeImage = this.changeImage.bind(this);
this.cropImage = this.cropImage.bind(this);
}
changeImage(e) {
e.preventDefault();
let files;
if (e.dataTransfer) {
files = e.dataTransfer.files;
} else if (e.target) {
files = e.target.files;
}
const reader = new FileReader();
reader.onload = () => {
this.setState({ src: reader.result });
};
reader.readAsDataURL(files[0]);
}
cropImage() {
if (typeof this.cropper.getCroppedCanvas() === 'undefined') {
return;
}
this.setState({
cropResult: this.cropper.getCroppedCanvas().toDataURL(),
});
console.log(this.cropper.getCroppedCanvas().toDataURL());
}
render() {
var {selectedClub} = this.props;
return (
<div className="uploadImages">
<div className="Image" style={{ width: '100%' }}>
<br />
<h3>Add Images</h3><br />
<input type="file" multiple onChange={this.changeImage}/>
<ClubCrop srcs={this.state.src} key="cropping" onClick={this.cropImage.bind(this)} cropper={cropper => { this.Cropper = cropper; }} crop={this.cropper}/>
</div>
</div>
);
}
}
And my component :
export default props => {
return (
<div style={{ width: '50%'}}>
<div className="inputImage" style={{ width: '5%', float: 'left' }}>
<Cropper
style={{height: 800, width: '100%'}}
aspectRatio={4 / 3}
preview=".img-preview"
guides={false}
src={props.srcs}
ref={props.cropper}
/>
</div>
<div className="box" style={{ width: '50%', float: 'right' }}>
<h3>Preview</h3>
<div className="img-preview" style={{ width: '100%', float: 'left', height: 300 }}></div>
</div>
<div className="cropButton">
<button onClick={props.onClick}>Crop Image</button>
</div>
</div>
);
}
Thank you!

Resources