I have this component that loads an image and then only displays content after the image is loaded (checks for image loading state). How do a write a jest test to test that it is working properly and that the content only displays after the image has loaded and not before?
import HeaderImage from '../images/headerimage.svg';
const ImageStyled = styled.img`
margin-left: -0.1rem;
`;
export default class ImageDisplay extends Component {
constructor(props) {
super(props);
this.state = { imageStatus: 'loading' };
}
handleImageLoaded() {
this.setState({ imageStatus: 'loaded' });
}
renderContent = () => (
if (this.state.imageStatus === 'loading') {
return null;
}
else {
<div>
Some Sample Content
</div>
}
render() {
return (
<div>
<ImageStyled
src={HeaderImage}
onLoad={() => {
this.handleImageLoaded();
}}
/>
{this.renderContent()}
</div>
);
}
}
`
Related
So I am quite new to React world, and I have this problem I am trying to solve, but I don't quite understand why it is happening.
So I want to pass the state of component to parent component and from parent component to child component and everything look okay, and in console log the state goes trough, but nothing changes. I believe there is a way I need to listen for state change or something within child component so it works. If I put true in the parent component, child component also get's true, but if I toggle it on click, it goes trough but nothing changes in the child component.
Also I understand my code is little rough right now ill reafactor it later, but right now I am trying to understand why it does not work.
If anyone could help me I would be thankful for it.
This is component that controls the state.. So the state passes from TurnOnBtn to App and from App it goes to TodoList
import "./Todo.css";
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.state = { display: false };
this.handleState = this.handleState.bind(this);
}
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
export default TurnOnBtn;
parent component App
import TurnOnBtn from "./TurnOnBtn";
import TheMatrix from "./TheMatrxHasYou";
import TodoList from "./TodoList";
import { Component } from "react";
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.checkDisplay = this.checkDisplay.bind(this);
}
checkDisplay(newDisplay) {
this.setState({
display: newDisplay,
});
console.log(this.state);
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn checkDisplay={this.checkDisplay} />
</div>
);
}
}
export default App;
child component TodoList
import Todo from "./Todo";
import NewTodoForm from "./NewTodoForm";
import { v4 as uuid } from "uuid";
import "./Todo.css";
class TodoList extends Component {
constructor(props) {
super(props);
this.state = {
todos: [],
displayOn: this.props.display,
};
this.newTodo = this.newTodo.bind(this);
this.editTodo = this.editTodo.bind(this);
this.deleteTodo = this.deleteTodo.bind(this);
}
editTodo(id, updatedTask) {
const updatedTodo = this.state.todos.map((todo) => {
if (todo.id === id) {
return { ...todo, todo: updatedTask };
}
return todo;
});
this.setState({
todos: updatedTodo,
});
console.log(updatedTask);
}
deleteTodo(id) {
this.setState({
todos: this.state.todos.filter((todo) => todo.id !== id),
});
}
newTodo(newState) {
this.setState({
todos: [...this.state.todos, { ...newState }],
});
}
render() {
return (
<div
style={this.state.displayOn ? { opacity: 1 } : { opacity: 0 }}
className="Todo-screen"
>
{" "}
<div className="TodoList">
<div className="TodoList-todos">
{" "}
{this.state.todos.map((todo) => (
<Todo
key={uuid()}
id={todo.id}
active={todo.active}
editTodo={this.editTodo}
deleteTodo={this.deleteTodo}
todoItem={todo.todo}
/>
))}
</div>
</div>{" "}
<NewTodoForm newTodo={this.newTodo} />
</div>
);
}
}
export default TodoList;
The bug here is in these line of codes:
handleState() {
this.setState({ display: !this.state.display });
this.props.checkDisplay(this.state.display);
}
Remember setState is an async function, so by the time you set a new state using setState, the value for this.state is not guaranteed changed.
One way to fix this is using the setState callback, which will run after the state is changed:
handleState() {
this.setState({ display: !this.state.display }, function() {
this.props.checkDisplay(this.state.display);
});
}
But you don't need to use another state to keep display state in TurnOnBtn as you can pass the toggle callback from the parent:
App.js
class App extends Component {
constructor(props) {
super(props);
this.state = { display: true };
this.toggleDisplay = this.toggleDisplay.bind(this);
}
toggleDisplay() {
this.setState({
display: !this.state.display,
});
}
render() {
return (
<div className="App">
<TodoList display={this.state.display} />
<TheMatrix />
<TurnOnBtn toggleDisplay={this.toggleDisplay} />
</div>
);
}
}
TurnOnBtn.js
class TurnOnBtn extends Component {
constructor(props) {
super(props);
this.handleState = this.handleState.bind(this);
}
handleState() {
this.props.toggleDisplay();
}
render() {
return (
<button onClick={this.handleState} className="TurnOnBtn">
<i className="fa fa-power-off"></i>
</button>
);
}
}
I'm also using semantic-ui-react. When I pass the child component down from the parent the css styling gets all messed up, I lose my images and the click doesn't work.
I can call the cardClickHandler method in the parent component and am console logging the correct child, i just can't get it to render (am not hitting the console.log in the child component).
I also tried to run the cardClickHandler method in the images container to pass it down but that didn't work.
please help and explain what i'm doing wrong. thanks!
images container:
import React from 'react';
import SearchBar from '../components/SearchBar';
import Images from '../components/Images';
import ImageCard from '../components/ImageCard';
class ImagesContainer extends React.Component {
state = {
images: [],
image: {},
sortValue: '',
inputValue: '',
};
componentDidMount() {
fetch('http://localhost:3000/images').then((resp) => resp.json()).then((resp) => {
this.setState({
images: resp
});
});
}
imageFilterOnChange = (event) => {
this.setState({
inputValue: event.target.value
});
};
sortImages = (images) => {
if (this.state.sortValue === 'location') {
return [ ...images ].sort((a, b) => {
if (a.location > b.location) {
return 1;
} else if (a.location < b.location) {
return -1;
} else {
return 0;
}
});
} else {
return images;
}
};
render() {
const filteredImages = this.state.images.filter((image) => {
return image.location.toLowerCase().includes(this.state.inputValue.toLowerCase());
});
return (
<div>
<Images
images={this.sortImages(filteredImages)}
onClick={this.cardClickHandler}
/>
<SearchBar
images={this.sortImages(filteredImages)}
imageFilterOnChange={this.imageFilterOnChange}
inputValue={this.state.inputValue}
onChange={this.handleSortImages}
/>
</div>
</div>
);
}
}
export default ImagesContainer;
parent component:
import React from 'react';
import ImageCard from './ImageCard';
import { Card, Image } from 'semantic-ui-react';
class Images extends React.Component {
state = {
image: []
};
cardClickHandler = (e) => {
let cardId = e.target.dataset.id;
this.props.images.find((image) => {
return image.id === cardId;
});
console.log('hi, cardId', cardId);
fetch(`http://localhost:3000/images/${cardId}`)
.then((resp) => resp.json())
.then((resp) => {
this.setState({
image: resp
})
console.log(this.state.image);
})
}
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<Card
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</Card>
);
});
}
}
export default Images;
child component:
i'm not hitting the console.log here, so no more code!
import React from 'react';
import { Card, Image } from 'semantic-ui-react';
class ImageCard extends React.Component {
render() {
console.log('image card');
return (
<Card>
</Card>
);
}
}
export default ImageCard;
I left a comment with a few improvements to the code you could make. Specifically:
You have an extra </div> in your ImagesContainer.
Also, you'll want to remove onClick={this.cardClickHandler} from ImagesContainer as cardClickHandler is defined not on ImagesContainer but instead on your Images component.
But the problem is that you are not rendering your ImageCard component at all. You are just rendering <Card> instead of <ImageCard>
Specifically, your parent component's render should change from this:
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<Card
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</Card>
);
});
}
to this:
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<ImageCard
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</ImageCard>
);
});
}
I know the title might be confusing, as well as might sound as a repeat, please read the whole description before marking it as repeat, I am new to react and need some help.
I am building a dashboard. I have a navigation bar div which has multiple tabs and a content div which has the corresponding content. Once a tab is clicked i render its corresponding content. Within any tab the user can do various things/changes. Lets say i have a tab ABC which when clicked produces an initial view, when i click this tab again when it is already clicked i need to re-render the ABC tabs content.
Essentially what i want to do is when after clicking test and test-demo once, user clicks test again the text 'test-demo' should disappear.
import React, { Component } from 'react';
const Button = (props) => {
return (
<button onClick={() => props.onClick(props.buttonName.trim())}>{props.buttonName}</button>
);
};
class Test extends Component {
static initialState = () => ({
appContent:null,
});
state = Test.initialState();
switchTab = (buttonKey) => {
this.setState(prevState => ({
appContent:<a>{buttonKey}</a>
}));
}
render() {
let appContent = null;
switch(this.props.navigationTab) {
case "test":
appContent = <Button onClick={this.switchTab} buttonName='test-demo' />
break;
default:
appContent = null
break;
};
return (
<div>
{appContent}
{this.state.appContent}
</div>
);
}
}
class AppContent extends Component {
render() {
return (
<div>
<Test navigationTab={this.props.navigationTab}/>
</div>
);
}
}
class App extends Component {
static initialState = () => ({
navigationTab:null,
});
state = App.initialState();
switchTab = (buttonKey) => {
this.setState(prevState => ({
navigationTab:buttonKey,
}));
}
render() {
return (
<div>
<div>
<Button onClick={this.switchTab} buttonName='test'/>
</div>
<AppContent navigationTab={this.state.navigationTab} />
</div>
);
}
}
export default App;
https://stackblitz.com/edit/react-fs8u7o?embed=1&file=index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
const Button = (props) => {
return (
<button onClick={() => props.onClick(props.buttonName.trim())}>{props.buttonName}</button>
);
};
class Test extends Component {
constructor(props) {
super(props);
this.state = {
appContent: null,
hideTestDemo:false,
};
}
componentWillReceiveProps(nextProps){
this.setState(prevState => ({
hideTestDemo:nextProps.hideTestDemo,
}));
}
switchTab = (buttonKey) => {
this.setState(prevState => ({
appContent: <a>{buttonKey}</a>,
hideTestDemo:false,
}));
}
render() {
let appContent = null;
switch (this.props.navigationTab) {
case "test":
appContent = <Button onClick={this.switchTab} buttonName='test-demo' />
break;
default:
appContent = null
break;
};
return (
<div>
{appContent}
{(!this.state.hideTestDemo ) ? this.state.appContent:null}
</div>
);
}
}
class AppContent extends Component {
render() {
return (
<div>
<Test {...this.props} />
</div>
);
}
}
class App extends Component {
constructor() {
super();
this.state = {
navigationTab: null,
};
}
hideTestDemo = false;
switchTab = (buttonKey) => {
if (this.hideTestDemo)
this.setState(prevState => ({
navigationTab: buttonKey,
hideTestDemo: true,
}));
else
this.setState(prevState => ({
navigationTab: buttonKey,
hideTestDemo:false,
}));
this.hideTestDemo=!this.hideTestDemo;
}
render() {
return (
<div>
<div>
<Button onClick={this.switchTab} buttonName='test' />
</div>
<AppContent {...this.state} />
</div>
);
}
}
render(<App />, document.getElementById('root'));
I have try to upload image to Redux and show in of React-Konva in many ways. But it isn't work. Both in base64 and blob. But in normal situation like using component's state to keep data(base64) it's work. I don't know why.
In my component just have button for upload and React-Konva Component for show image
this is error from base64 store in redux and show to Image Tag
class UploadButton extends Component{
constructor(props){
...
this.handleUpload = this.handleUpload.bind(this);
}
handleUpload({target}){
const reader = new FileReader();
const file = target.files[0];
reader.onloadend = () => {
this.props.dispatch({
type: 'UPLOAD_IMAGE',
image: reader.result,
});
};
reader.readAsDataURL(file);
}
render(){
return(
<div>
<input
value="Upload"
type="button"
onClick={ () => { this.uploadInput.click() } }
/>
<input
id="inputUpload"
ref={ (ref) => { this.uploadInput = ref } }
type="file"
style={ { display: 'none' } }
onChange = { (event) => { this.handleUpload(event) }}
/>
</div>
);
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Stage, Layer, Image } from 'react-konva';
class ShowImage extends Component {
constructor(props){
super(props);
this.props = props;
this.state = {
image : null,
}
}
render(){
return (
<Stage
width={ 500 }
height={ 500 }
>
<Layer>
<Image image={ this.props.image} ></Image>
</Layer>
</Stage>
);
}
}
const mapStateToProps = ( state ) => ({
image : state.image,
});
export default connect(mapStateToProps)(ShowImage);
To use the image in react-konva you have to create a native instance of window.Image.
class VaderImage extends React.Component {
state = {
image: new window.Image()
};
componentDidMount() {
this.state.image.src = this.props.image;
this.state.image.onload = () => {
// need to update layer manually
this.imageNode.getLayer().batchDraw();
};
}
render() {
return (
<Image
image={this.state.image}
y={250}
ref={node => {
this.imageNode = node;
}}
/>
);
}
}
https://konvajs.github.io/docs/react/Images.html
I'm displaying a overlay page when a certain input is clicked. Now I want to remove that overlay page when a user clicks somewhere in that overlay. How can I do that?
I'm displaying the overlay on click like this
constructor(props) {
super(props);
this.state = {
showComponent: false,
};
this.popup_ques = this.popup_ques.bind(this);
}
popup_ques() {
this.setState({
showComponent: true,
});
}
render() {
return (
<div className="ff">
<div className="middle_div">
<input className='post_data_input' placeholder="Ask your question here" ref="postTxt" onClick={this.popup_ques}/>
</div>
{this.state.showComponent ? <QuestionOverlay/> : null}
</div>
);
}
My overlay is in the component QuestionOverlay
class QuestionOverlay extends Component {
constructor() {
super();
}
closeOverLay = (e) => {
alert("fse");
}
render() {
return (
//Here I have implemented my overlay
)
}
}
export default QuestionOverlay;
So how can I close/remove the overlay component when I click somewhere on my overlay?
Pass a function from the Overlay's parent component (the component which displays the Overlay) that is called onClick in the Overlay. This function will update this.state.showComponent of the parent to false to hide the Overlay.
Parent
constructor(props) {
super(props);
this.state = {
showComponent: false,
};
this.popup_ques = this.popup_ques.bind(this);
this.hide_overlay = this.hide_overlay.bind(this);
}
popup_ques() {
this.setState({
showComponent: true,
});
}
hide_overlay() {
this.setState({
showComponent: false
})
}
render() {
return (
<div className="ff">
<div className="middle_div">
<input className='post_data_input' placeholder="Ask your question here" ref="postTxt" onClick={this.popup_ques}/>
</div>
{this.state.showComponent && <QuestionOverlay hideOverlay={this.hide_overlay} />}
</div>
);
}
Overlay
class QuestionOverlay extends Component {
constructor() {
super();
}
closeOverLay = (e) => {
alert("fse");
}
render() {
return (
<div onClick={this.props.hideOverlay}>
// Overlay content
</div>
)
}
}
export default QuestionOverlay;