Speeding up react Component rendering - reactjs

I have a react component that renders fine but as soon as i add images it renders far slower and causes a few hundred miliseconds delay on the component rendering. Does anyone know a way to speed this up or what I am doing wrong?
class SearchResults extends React.Component {
render() {
//Individual container styling
const divStyle = {
float: 'left',
width: '80px',
height: '80px',
border: '5px solid pink',
borderRadius: '3px',
margin: '2px',
cursor: 'pointer',
backgroundColor: 'white'
};
const styleImage = {
maxWidth: '70px',
maxHeight: '70px',
margin: 'auto',
display: 'flex'
}
//Individual record
const startItem = (this.props.itemsPerPage * this.props.page) - this.props.itemsPerPage;//First item on page
const endItem = startItem + this.props.itemsPerPage;
//Slice is from/to
const all = searchableDatabase.slice(startItem, endItem).map((value, index) => {
return (
<div style={divStyle} >
{value.edit_title_normal}
{/*<img style={styleImage} src={'YourMemories/' + currentCollection.id + '/' + value.sequentialNumber + '/' + value.sequentialNumber + ' Thumbnail.jpg'} />*/}
</div>
)
});
//Main container
return (
<div>
<div style={{ clear: 'both', paddingTop: '20px', paddingBottom: '20px', textAlign: 'center' }}>
<Pagination allLength={searchableDatabase.length} page={this.props.page} itemsPerPage={this.props.itemsPerPage} />
</div>
<div>
{all}
</div>
</div>
);
}
}

Related

update a value using useRef and useEffect

I need to get the left position of an element after elements got mounted to show a progress bar. It works on clicking links however when it got mounted the progressWidth is not getting calculated. basically, the useEffect seems running before the component is mounted!!!!
export default ({ task, selectedLocal, selectedScenarioId, taskId, selectedTaskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const isBefore = taskId <= selectedTaskId;
const isActive = taskId === selectedTaskId;
const navItemWidth = 100;
const [progressWidth, setProgressWidth] = useState(0);
const ref = useRef<HTMLInputElement>(null);
useEffect(() => {
if (ref.current) {
console.log(ref.current.getBoundingClientRect().left);
setProgressWidth(ref.current.getBoundingClientRect().left);
}
});
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold'
},
navTitle: {
width: `${navItemWidth}px`,
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: `${navItemWidth}px`
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative',
'z-index': '3'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
},
activeNavIndicator: { borderColor: theme.palette.themePrimary },
activeInnerIndicator: { backgroundColor: theme.palette.themePrimary },
progress: {
marginTop: '59px',
'z-index': '2',
position: 'fixed',
left: '0',
width: `${progressWidth}px`,
borderBottom: '2px solid',
borderColor: theme.palette.themePrimary
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(isActive ? [styles.navLink, styles.navLinkActive] : styles.navLink)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div
ref={ref}
className={css(
isBefore ? [styles.navIndicator, styles.activeNavIndicator] : styles.navIndicator
)}
>
<div
className={css(
isBefore ? [styles.innerIndicator, styles.activeInnerIndicator] : styles.innerIndicator
)}
/>
</div>
</div>
</NavLink>
{isActive && <div className={css(styles.progress)} />}
</div>
);
};
So when component is getting loaded I get image 1, when I click on the component I get image 2. What I need to happen is when component is getting loaded it should look like image 2.

Creating Tweet Display Box in ReactJS

I am using the following code to generate a Tweet input box which takes in text/video/image/emoji. And they can be in different combinations.
I am not sure how to generate a tweet display box which shows the final display containing text/image/emoji ? I understand I might need to put the different inputs in an array or some sort but what after that. My current code for display side is performing nothing and I am not sure where to go from here.
I am looking for display box to be of following form after a Submit Button:
Code components/EmojiPicker.js has:
import React, {useState} from 'react'
import ReactDOM from "react-dom";
import { Picker } from "emoji-mart";
import Button from "#material-ui/core/Button";
const EmojiPicker = ({ onSelect }) => {
const [show, setShow] = useState(false);
return (
<>
<Button
onClick={() => setShow(oldState => !oldState)}
style={{ width: "30px", height: "30px", borderRadius: "4px", border: "3px solid", display: "flex", alignItems: "center", justifyContent: "center",
background: "transparent"}}>
ej
</Button>
{ReactDOM.createPortal(
show && <Picker onSelect={onSelect} />,
document.body
)}
</>
);
};
export default EmojiPicker
Code components/FileInput.js has:
import React, {useRef} from 'react'
const FileInput = ({ onChange, children }) => {
const fileRef = useRef();
const onPickFile = event => {
onChange([...event.target.files]);
};
return (
<div
style={{
width: "35px",
height: "35px",
borderRadius: "3px"
}}
onClick={() => fileRef.current.click()}
>
{children}
<input
multiple
ref={fileRef}
onChange={onPickFile}
type="file"
style={{ visibility: "hidden" }}
/>
</div>
);
};
export default FileInput
Code components/tweetboxImgInp.js as:
import React, {useState, useEffect} from 'react'
const ImgIcon = () => (
<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M14 13l4 5H6l4-4 1.79 1.78L14 13zm-6.01-2.99A2 2 0 0 0 8 6a2 2 0 0 0-.01 4.01zM22 5v14a3 3 0 0 1-3 2.99H5c-1.64 0-3-1.36-3-3V5c0-1.64 1.36-3 3-3h14c1.65 0 3 1.36 3 3zm-2.01 0a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h7v-.01h7a1 1 0 0 0 1-1V5z" />
</svg>
);
export const Img = ({ file, onRemove, index }) => {
const [fileUrl, setFileUrl] = useState(null);
useEffect(() => {
if (file) {
setFileUrl(URL.createObjectURL(file));
}
}, [file]);
return fileUrl ? (
<div style={{ position: "relative", maxWidth: "230px", maxHeight: "95px" }}>
<img
style={{
display: "block",
maxWidth: "230px",
maxHeight: "95px",
width: "auto",
height: "auto"
}}
alt="pic"
src={fileUrl}
/>
<div
onClick={() => onRemove(index)}
style={{
position: "absolute",
right: 0,
top: 0,
width: "20px",
height: "20px",
borderRadius: "50%",
background: "black",
color: "white",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
x
</div>
</div>
) : null;
};
export default ImgIcon
And App.js has:
import React, { useRef, useState } from "react";
import ImgIcon, {Img} from './components/tweetboxImgInp'
import EmojiPicker from './components/EmojiPicker'
import FileInput from './components/FileInput'
import "emoji-mart/css/emoji-mart.css";
import "./styles.css";
function App() {
const [text, setText] = useState("");
const [pics, setPics] = useState([]);
const textAreaRef = useRef();
const insertAtPos = value => {
const { current: taRef } = textAreaRef;
let startPos = taRef.selectionStart;
let endPos = taRef.selectionEnd;
taRef.value =
taRef.value.substring(0, startPos) +
value.native +
taRef.value.substring(endPos, taRef.value.length);
};
return (
<div style={{display: "flex", flexDirection: "column", border: "3px solid", borderRadius: "5px", width: "600px", minHeight: "200px", padding: "20px"}} >
<div style={{ display: "flex", flexDirection: "column", flex: 1, border: "1px solid", borderRadius: "5px", margin: "0px"}}>
<textarea
ref={textAreaRef}
value={text}
style={{ flex: 1, border: "none", minHeight: "150px" }}
onChange={e => setText(e.target.value)}
/>
<div style={{ display: "flex", flexDirection: "row", flexWrap: "wrap", background: "fbfbfb"}} >
{pics.map((picFile, index) => (
<Img key={index} index={index} file={picFile} onRemove={rmIndx =>
setPics(pics.filter((pic, index) => index !== rmIndx))}/>))}
</div>
</div>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", marginTop: "20px" }}>
<div style={{ marginRight: "20px" }}>
<FileInput onChange={pics => setPics(pics)}>
{/* <ImgIcon /> */}
Tes
</FileInput>
</div>
<EmojiPicker onSelect={insertAtPos} />
</div>
</div>
);
}
export default App
Edit: I am good with the display box accepting only 1 media file, text and few emoji. It will surprise me if I am the only one in 2019 looking to do it for fun.
Working Example
Click the codesandbox button to view the demo
The tweet display component is pretty straightforward. Its a flexbox column with two parts. First part of the column contains the tweet. The second part of the column contains the list of images/media elements. Emoji is part of the text component.
Tweet Display Component
const Tweet = ({ tweet: { text, images } }) => (
<div
style={{
margin: "20px",
border: "1px solid grey",
width: "600px",
padding: "20px",
borderRadius: "3px"
}}
>
<div>{text}</div>
{images.length > 0 && (
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
background: "fbfbfb",
padding: "30px 0"
}}
>
{images.map((img, i) => (
<Img key={i} file={img} index={i} isSingle={images.length === 1} />
))}
</div>
)}
</div>
);
For more info checkout this css-tricks article to get more info on css flex layout

Building Twitter Like Dialogbox for input

I am learning ReactJS and Material-UI and hence I have been working to build a semi twitter clone.
I am now hitting a road block in the sense that I can't figure how to build this input dialog box which can take all of text, video, photo, gif in same box.
Using <Input /> I can provide individually what kind of input I have i.e. email, password, etc using type. But I am not sure how to design this particular dialog box to take multiple inputs.
Can you please show me a working code example ?
Its 2019 and a lot of things have changed. This is a very rough and hacky implementation of how twitter has implemented. Its pretty simple.
Working Demo Link
For folks who want to just look at the code head over to codesandbox
Note: this was done very quickly to demonstrate what twitter has done under the hood.
The editor mainly consists of a <textarea /> where text for the tweets are added.
Below the text area is an expanding div block which loops image files that are selected from file system.
As for the emoji, a standard emoji picker is used to select emojis and plain old javascript is used to add emojis at the current cursor position in the textarea.
I have skipped the gif picker, as its similar to the image picker, the only difference being, a modal is open to populate gifs from giphy. An api from giphy. can be easily integrated to achieve this.
The following are the components written
FileInput Component
The FileInput component is used as the button and file picker to select images. Its a plain <input type="file" />. The native styles are hidden and a custom icon is displayed
const FileInput = ({ onChange, children }) => {
const fileRef = useRef();
const onPickFile = event => {
onChange([...event.target.files]);
};
return (
<div
style={{
width: "35px",
height: "35px",
borderRadius: "3px"
}}
onClick={() => fileRef.current.click()}
>
{children}
<input
multiple
ref={fileRef}
onChange={onPickFile}
type="file"
style={{ visibility: "hidden" }}
/>
</div>
);
};
Img Component
The Img component displays the images selected from the input. It uses URL.createObjectURL to create a temporary local url that can be populated inside an img tag. This is the image preview seen in the tweet sheet below the textarea.
const Img = ({ file, onRemove, index }) => {
const [fileUrl, setFileUrl] = useState(null);
useEffect(() => {
if (file) {
setFileUrl(URL.createObjectURL(file));
}
}, [file]);
return fileUrl ? (
<div style={{ position: "relative", maxWidth: "230px", maxHeight: "95px" }}>
<img
style={{
display: "block",
maxWidth: "230px",
maxHeight: "95px",
width: "auto",
height: "auto"
}}
alt="pic"
src={fileUrl}
/>
<div
onClick={() => onRemove(index)}
style={{
position: "absolute",
right: 0,
top: 0,
width: "20px",
height: "20px",
borderRadius: "50%",
background: "black",
color: "white",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
x
</div>
</div>
) : null;
};
App (Tweet Sheet)
This is the root component that stitches everything together.
function App() {
const [text, setText] = useState("");
const [pics, setPics] = useState([]);
const textAreaRef = useRef();
const insertAtPos = value => {
const { current: taRef } = textAreaRef;
let startPos = taRef.selectionStart;
let endPos = taRef.selectionEnd;
taRef.value =
taRef.value.substring(0, startPos) +
value.native +
taRef.value.substring(endPos, taRef.value.length);
};
return (
<div
style={{
display: "flex",
flexDirection: "column",
border: "3px solid",
borderRadius: "5px",
width: "600px",
minHeight: "200px",
padding: "20px"
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
flex: 1,
border: "1px solid",
borderRadius: "5px",
margin: "0px"
}}
>
<textarea
ref={textAreaRef}
value={text}
style={{ flex: 1, border: "none", minHeight: "150px" }}
onChange={e => setText(e.target.value)}
/>
<div
style={{
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
background: "fbfbfb"
}}
>
{pics.map((picFile, index) => (
<Img
key={index}
index={index}
file={picFile}
onRemove={rmIndx =>
setPics(pics.filter((pic, index) => index !== rmIndx))
}
/>
))}
</div>
</div>
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
marginTop: "20px"
}}
>
<div style={{ marginRight: "20px" }}>
<FileInput onChange={pics => setPics(pics)}>
<ImgIcon />
</FileInput>
</div>
<EmojiPicker onSelect={insertAtPos} />
</div>
</div>
);
}
EmojiPickerModal
const EmojiPicker = ({ onSelect }) => {
const [show, setShow] = useState(false);
return (
<>
<button
onClick={() => setShow(oldState => !oldState)}
style={{
width: "30px",
height: "30px",
borderRadius: "4px",
border: "3px solid",
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "transparent"
}}
>
ej
</button>
{ReactDOM.createPortal(
show && <Picker onSelect={onSelect} />,
document.body
)}
</>
);
};
Note: For the emoji picker a popular open source emoji picker component emoji-mart was used

How to style the active link childs using CSS in JS and React Router?

I am using React ROuter and CSS in JS for style.
I'd like to change the background color and border color for the active links child divs (navIndicator and innerIndicator)
import { css, StyleSheet } from 'aphrodite/no-important';
export default ({ task, selectedLocal, selectedScenarioId, taskId }: ITaskNavItem) => {
const isActiveNav = (match: any, location: object) => match;
const theme = getTheme();
const styles = StyleSheet.create({
navLink: {
display: 'flex',
fontSize: '12px',
textDecoration: 'none',
color: theme.palette.neutralPrimary
},
navLinkActive: {
color: theme.palette.neutralPrimary,
fontWeight: 'bold',
'.navIndicator': {
borderColor: theme.palette.themePrimary
},
'.innerIndicator': {
backgroundColor: theme.palette.themePrimary
}
},
navTitle: {
width: '100px',
textAlign: 'center',
wordBreak: 'break-word',
wordSpacing: '100px'
},
linkText: {
display: 'flex',
flexFlow: 'column',
'align-items': 'center'
},
navIndicator: {
borderRadius: '50%',
margin: '10px 0 0 0',
backgroundColor: theme.palette.white,
width: '30px',
height: '30px',
border: '2px solid',
borderColor: theme.palette.neutralPrimary,
position: 'relative'
},
innerIndicator: {
position: 'absolute',
borderRadius: '50%',
width: '20px',
height: '20px',
backgroundColor: theme.palette.neutralPrimary,
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)'
}
});
return (
<div className={css(styles.navLink)}>
<NavLink
exact
isActive={isActiveNav}
className={css(styles.navLink)}
activeClassName={css(styles.navLinkActive)}
to={`/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}`}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div className={css(styles.navIndicator)}>
<div className={css(styles.innerIndicator)} />
</div>
</div>
</NavLink>
</div>
);
};
However, the navIndicator and innerIndicator colors doesn't change when nav link is active.
Wondering how to get the style working for active link?
NavLink element does not indicate to its children if it active. So I may suggest to get currecnt route from BrowserRouter component (your component should be child of BrowserRouter so NavLink works), compare path and set local isActive variable to indicate if specific route is active.
For example (not tested, just sample):
const StyledLinks: React.FunctionComponent<RouteComponentProps & ITaskNavItem> = ({ task, selectedLocal, selectedScenarioId, taskId, location }) => {
const to = '/selectedLocal/${selectedLocal}/scenarios/${selectedScenarioId}/tasks/${taskId}';
const isActive = to === location.pathname;
const styles = StyleSheet.create({
// ...
navIndicatorActive: {
borderColor: theme.palette.themePrimary
},
// ...
return (
<div className={css(styles.navLink)}>
<NavLink
exact
className={css(styles.navLink)}
activeClassName={css(styles.navLinkActive)}
to={to}
>
<div className={css(styles.linkText)}>
<div className={css(styles.navTitle)}> {task.title}</div>
<div className={isActive ? css([styles.navIndicator, styles.navIndicatorActive]) : css(styles.navIndicator)}>
<div className={css(styles.innerIndicator)} />
</div>
</div>
</NavLink>
</div>
);
}
// Wrap your component withRouter to get location prop
export default withRouter(StyledLinks);

Reactjs components error

I am completely new to React and I need to make an infinite scroll effect by including some divs from another component. I have found an infinite scroll library online and I am using it here in the index.js file. I have created my divs in Home.js but I am getting some errors like:-
Error: Mandatory prop "refreshFunction" missing. and TypeError: this.props.next is not a function. Can someone help me out please. I think even though I am exporting Home.js and then importing it in index.js but it is still not able to invoke the methods. Can someone please let me know what I am doing wrong and how to correct it? I have added the code below.
index.js
import React from 'react';
import { render } from "react-dom";
import InfiniteScroll from 'react-infinite-scroll-component';
import { Home } from "./components/Home";
class App extends React.Component{
render() {
return (
<InfiniteScroll
pullDownToRefresh
pullDownToRefreshContent={
<h3 style={{ textAlign: 'center' }}>↓ Pull down to refresh</h3>
}
releaseToRefreshContent={
<h3 style={{ textAlign: 'center' }}>↑ Release to refresh</h3>
}
refreshFunction={this.refresh}
next={this.generateDivs}
hasMore={true}
loader={<h4>Loading...</h4>}
endMessage={
<p style={{ textAlign: 'center' }}>
<b>Yay! You have seen it all</b>
</p>
}>
<Home/>
</InfiniteScroll>
);
}
}
render(<App />, window.document.getElementById("app"));
Home.js
import React from "react";
const style = {
display: 'flex',
alignItems: 'center',
fontSize: 40,
border: '2px solid red',
margin: ' 0 auto 10px auto',
textAlign: 'center'
}
const divs = [
<div key={1} style={{ height: 200, ...style }}>Div no 1</div>,
<div key={2} style={{ height: 200, ...style }}>Div no 2</div>,
<div key={3} style={{ height: 200, ...style }}>Div no 3</div>,
<div key={4} style={{ height: 200, ...style }}>Div no 4</div>,
<div key={5} style={{ height: 200, ...style }}>Div no 5</div>,
<div key={6} style={{ height: 200, ...style }}>Div no 6</div>,
<div key={7} style={{ height: 200, ...style }}>Div no 7</div>,
<div key={8} style={{ height: 200, ...style }}>Div no 8</div>,
];
export class Home extends React.Component{
constructor() {
super();
this.state = {
divs: divs
};
this.generateDivs = this.generateDivs.bind(this);
this.refresh = this.refresh.bind(this);
}
generateDivs() {
let moreDivs = [];
let count = this.state.divs.length;
console.log(count);
for (let i = 0; i < 30; i++) {
moreDivs.push(
<div key={'div' + count++} style={{ height: 200, ...style }}>
Div no {count}
</div>
);
}
setTimeout(() => {
this.setState({ divs: this.state.divs.concat(moreDivs) });
}, 500);
}
refresh() {
this.setState({ divs: [] });
setTimeout(() => {
this.setState({ divs });
}, 3000);
}
render() {
return (
<div>
{this.state.divs}
</div>
);
}
}

Resources