Update component styles dynamically from state on initial load? - reactjs

My app has theme variations that are stored in a database that id like to update many elements on initial render only, but have it carried over to every view. It seems it would make sense to update the top level app container components style with state, but this seems to produce an endless loop. Is there something im doing wrong or is there a easier way to achieve this? I do not want to use inline styles, or run these styles to 50 components across the app.
example of what im trying currently with one style:
const StyledApp = styled.div`
body .pagination a { background-color: ${this.state.color}}
`;
class App extends React.Component {
constructor(props) {
super(props);
this.state = { color:"" };
autoBind(this);
}
componentDidMount() {
this.setColorState()
}
setColorState() {
var color = //get color from db
this.setState({ color:color});
}
render() {
const { props, state, setAfterLoginPath } = this;
return (
<StyledApp ready={this.state.ready} loading={props.loading}>
//app code
</StyledApp>
)
}
}

you can do something like styled components doc says passing props to style
const StyledApp = styled.div`
body .pagination a { background-color: ${props=>props.color}}
<StyledApp ready={this.state.ready} loading={props.loading} color={this.state.color}>
//app code
</StyledApp>

Related

How to create a simple spinning animation for an image in React

I'm using reactjs with material-ui styling, to create a dapp and have an image (circular in shape) which i want to spin continuously which customizable spin speed, maintaining a smooth flow. The spin speed needs to be customizable in which i feed a speed value to the component and it'd spin in that speed accordingly. Any ideas how to go by? Thanks.
PS: this is not related to 'loading components', loading animation, or loading image in any way. also, a solution which can be implemented using withStyles() of material-ui would be preferred. Thanks.
I'm writing this answer with respect to my comments above:
First, define a css animation keyframe to do a spin:
#keyframes spin {
from {transform:rotate(0deg);}
to {transform:rotate(360deg);}
}
Next, in your constructor, define the speed value:
constructor(props) {
super(props);
this.state = {
speed: 3
}
}
finally, make use of inline styling in ReactJS to pass a custom speed value from state (or sth like this.props.customSpdProps if you want to pass from props):
<img style={{animation: `spin ${this.state.speed}s linear infinite`}} src={SampleImg} alt="img"/>
Control spin speed by setting an initial property then propagate it to a react hook to be dynamically changed.
const SPEED = 0;
const kind = React.createElement,
container = "react-container";
const tags = {
cat : document.querySelector('[data-cat]'),
burger : document.querySelector('[data-burger]')
}
class Main extends React.Component {
constructor(props){
super(props)
}
componentDidMount() {
alert("say cheeze!")
}
render() {
const kinds = [];
Object.keys(tags).map(function(key, index) {
const targets = {
key : index,
tag : tags[key].tagName.toLowerCase(),
src : tags[key].src,
SPEED
}
kinds.push(kind(Spin, targets))
});
return kinds
}
}
const Spin = function(props) {
const [speed, go] = React.useState(props.SPEED);
const startSpin = function() {
go(speed + 1)
};
React.useEffect(function() {
startSpin()
}, []);
return kind(props.tag, { style : { animation : `spin ${speed}s linear infinite` }, src : props.src })
}
Demo https://gif.com.ai?id=QmagKQ16ZhwPMGunhWuiKydEJFW3y4MprxYeTNjbC87MxZ
From: https://dev.to/egfx/thinking-in-gif-with-react-5am0

How to capture click outside React component

I started to learn React and I am trying to implement a modal window. I am at the same time using TypeScript.
I wanted to capture a click outside my React component, so when I click outside the modal window, this one closes. I based my approach on this: How to capture click outside React component
import styled from 'styled-components';
const StyledModal = styled.div`
width: 100%;
background-color: #fff;
box-shadow: 0 0 0.625rem, rgba(0, 0, 0, 0.2);
#media (min-width: 576px) {
width: 32rem;
},
`;
class Modal extends React.Component<ModalProps> {
private modal: HTMLDivElement;
onOutsideClick = (e: any) => {
if (!_.isNil(this.modal)) {
if (!this.modal.contains(e.target)) {
this.onClose(e);
}
}
}
componentDidMount() {
document.addEventListener('mousedown', this.onOutsideClick, false);
}
componentWillMount() {
document.removeEventListener('mousedown', this.onOutsideClick, false);
}
render() {
<div>
<StyledModal ref={(node: any) => { this.modal = node; }}>
...
</StyledModal>
</div>
}
}
The issue is whenever I click inside or outside the modal I get this error, which I don't know what it is or how to fix it:
Any lights please let me know...
Since your StyledModal is styled-components you need to add innerRef to be able to get the DOM node. Keep in mind innerRef is a custom prop only for styled-components
https://github.com/styled-components/styled-components/blob/master/docs/tips-and-tricks.md#refs-to-dom-nodes
<StyledModal innerRef={(node: any) => { this.modal = node; }}>
...
</StyledModal>
From styled-components v4 onward it is ref prop.
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return (
<Input
ref={this.inputRef}
/>
);
}
For more info Refs
If you want to use a tiny component (466 Byte gzipped) that already exists for this functionality then you can check out this library react-outclick. It lets you capture clicks outside of a component.
The good thing about the library is that it also lets you detect clicks outside of a component and inside of another. It also supports detecting other types of events.

get height of image on load and send to parent

I am trying to get the height of an image when it has loaded and send it back to the parent component, but it is causing infinite rerendering.
This is a prototype of my code:
import MyImage from './images/myImage.jpg';
class Image extends React.Component {
constructor(props) {
super(props);
this.state = {
height: 0
}
}
getHeight = (e) => {
const height = e.target.getBoundingClientRect().height;
this.setState({
height: height
});
this.props.setUnitHeight(height);
}
render() {
const image = this.props.image;
return (
<img src={image.name} onLoad={(e)=>{this.getHeight(e)}} />;
);
}
}
class App extends Component {
constructor(props) {
super(props);
const initUnit = 78.4;
this.state = {
unit: initUnit
}
}
setUnitHeight = (height) => {
this.setState({
unit: height
});
}
render() {
return (
<div>
<Image image={MyImage} setUnitHeight={this.setUnitHeight} />
</div>
);
}
}
I have tried sending unit as a prop and then checking in shouldComponentUpdate whether it should be rerender or not, but that did nothing.
The issue you are having is that React by default re-renders the component every time you call this.setState. In your case what this is happening:
You load your Image component
It loads the <img> tag and fires the onLoad function
The onLoad function calls this.setState
Repeat these steps forever
You should take a look at the React's lifecycle components methods (https://reactjs.org/docs/react-component.html#the-component-lifecycle) to understand this better.
My suggestion is: do not keep the image height in the state, unless you really need it. If you really need to maintain it in the state for some reason you can use the lifecycle method shouldComponentUpdate (https://reactjs.org/docs/react-component.html#shouldcomponentupdate`) to prevent it from rendering.
Your code seems redundant, setState({}) isn't necessary in <Image> class. If you are using the same props throughout the app, then you should be setting it at one place and be using the same prop all over. For example -
getHeight = (e) => {
const height = e.target.getBoundingClientRect().height;
//setState not needed here
this.props.setUnitHeight(height);
}
That should do it.
P.S: Do check if your this references aren't going out of scope.

how can I select only one component of my array of component

this code here works but I don't know how to just click one of my component in >the array with this code I can change the color.
but I want to know how can I not change the color when I already change it in one >component thanks for the future answer
import React, { Component } from 'react';
export default class Seats extends Component {
constructor() {
super()
this.state = {
status: false,
};
}
changeColor(event) {
if (this.state.status === false) {
event.currentTarget.style.backgroundColor = '#D70202';
this.state.status = true;
}else {
this.state.status = false;
event.currentTarget.style.backgroundColor = '#0CB607';
}
}
render() {
let array = [];
for (var i = 0; i < 5; i++) {
array[i] = i;
}
const list = array.map((d, index) => <div className="seat" onClick={this.changeColor.bind(this)} key={index}></div>);
return (
<div>
{list}
</div>
);
}
}
.seat {
background-color: #0CB607;
border: 1px solid black;
height: 90px;
width: 90px;
}
There are two problems here, which need to be resolved separately:
Instead of using this.state.status = true|false you should use this.setState({ status: true|false }). This forces a re-render.
In your current approach, you are managing your state via just manipulating the DOM directly, setting the style.backgroundColor. This will get blown away the next time your component renders.
To address the second issue, I suggest storing the array of items you are manipulating as state at the component level. As an example:
JS:
export default class Seats extends React.Component {
constructor() {
super()
const seats = [...Array(5)].map(() => ({ status: false }))
this.state = { seats }
}
handleSeatClick(index) {
const seats = this.state.seats
const seat = seats[index]
seat.status = !seat.status
seats[index] = seat
this.setState({ seats })
}
render() {
return (
<div>{list.map((seat, index) =>
<div className={`seat ${seat.status ? 'highlight' : ''}`}
onClick={this.handleSeatClick.bind(index)}
></div>
</div>
)
}
}
CSS:
.seat {
background-color: #0CB607;
border: 1px solid black;
height: 90px;
width: 90px;
}
.seat.highlight {
background-color: #D70202;
}
In this example, we're persisting the array of seats in the component's state. If you are getting a pre-defined list of seats passed in, in the future, you could replace the line that creates the [...Array(5)]... bit with something that instead reads from props being passed in, or loads from an ajax call, etc.
Because the seats are persisted with their own state, as an array, we can simply inspect that state when rendering to determine whether to output the highlight CSS class - which applies the color.
One other thing you can refactor (which I didn't do, to keep this a clear explanation) is to get rid of the .bind in render entirely. Doing this is an anti-pattern, as it will re-create new functions for every item in the list, every time it renders.

How to create toggleable sidenav layout in React.js?

I am porting my layout from jQuery to React.js. This is very common one that consists of:
header with toggle button
sidenav with navigation links
content whose width adapts to sidenav state.
As you can imagine to achieve that a lot of (css) stuff is going on. I am really confused about possible approaches.
Here is mine:
class CoreLayout extends Component {
constructor(props) {
super(props)
this.state = {
sidenavCollapsed: false
}
}
onSidenavToggle() {
const { sidenavCollapsed } = this.state
document.body.classList.toggle('collapsed', !sidenavCollapsed)
this.setState({ sidenavCollapsed: !sidenavCollapsed })
}
render() {
const { sidenavCollapsed } = this.state
return (
<div>
<Header onSidenavToggle={::this.onSidenavToggle}></Header
<Sidenav>
<div className="content">content</div>
</div>
)
}
}
I do all the styling according to class attached to body element:
.collapsed .header {}
.collapsed .sidenav {}
.collapsed .content {}
Basically it's toggling sidenav width and content margin betwen 220 and 60.
So...
Should I pass collapsed property to each of layout elements and add class collapsed separately? What I am trying to achieve is similar to this.
What is the correct way of doing fade-out-in sidenav items animation? Till now I was using jQuery utilities, but I am not sure if directly using window.requestAnimationFrame() is correct. I have tried ReactCSSTransitionGroup with no success.
Just add a class to the navbar on button toggle and animate the transition using css.
See the demo
https://jsfiddle.net/kuLy0g8z/

Resources