I have a sidebar where it is possible to change the background of the sidebar as a background, I store pictures in the object, everything worked well for me until I decided to add a second property "color" inside the object, that is, in addition to changing the background, the color of the text also changes. it looks like this
I think that the essence is clear, but as I mentioned earlier, the problems started when I decided to add a second property, that is, the color of the text
The problem is that the color property does not work for me, that is, it works, but in this case the sidebar background does not work
My object looks very simple
import Bg from "../../SideBar/Background/mac.jpg"
import Pink from "../../SideBar/Background/pink.jpg"
import Anapa from "../../SideBar/Background/anapa.jpg"
const SideBarBackgrounds = [
{
SideBarWallpaper: Bg,
color: "red",
},
{
SideBarWallpaper: Pink,
color: "green",
},
{
SideBarWallpaper: Anapa,
color: "yellow",
},
].map((item) => {
return {
SideBarWallpaper: item.SideBarWallpaper,
color: item.color,
}
}
)
export default SideBarBackgrounds;
That is, I import the picture and apply it as the value for the SideBarWallpaper properties then create a second property named color and apply the string as the color
Then I use local storage to save the user changes.
export const CounterContext = createContext([]);
export default function ThemeDoc(props) {
const [SideBarBackground, SetSideBarBackground] = useState(JSON.parse(localStorage.getItem("BgKey")));
useEffect(() => {
localStorage.setItem("BgKey", JSON.stringify(SideBarBackground));
})
const ChangeSideBarWallpaper = (SideBarWallpaper) => {
localStorage.setItem('BgKey', JSON.stringify(SideBarWallpaper));
SetSideBarBackground(SideBarWallpaper);
}
return (
<div className="page-wrapper chiller-theme toggled">
<CounterContext.Provider value={{
SideBarWallpaperValue: [SideBarBackground, SetSideBarBackground],
}}>
<SideBar ChangeSideBarWallpaper={ChangeSideBarWallpaper} SideBarPageContent={SideBarPageContent} {...props} />
</CounterContext.Provider>
</div>
);
}
As you can see, I use React Context since I have a lot of themes and I import them in many files, in this case I deleted everything personal and left only the code that belongs to the background of the sidebar
Then I have a component called SideBarWallpaperContainer where I import my object, create a loop using a map, process each property from the object and finally import the SideBarWallpaperContainer component inside the SideBarModal component
export default function SideBarWallpaperContainer(props) {
const SideBarWallpaperList = SideBarBackgrounds.map((theme, index) => {
return (
<img key={index} width={"70"} height={"55"} src={theme.SideBarWallpaper}
className={"SideBar_Appearance_Select_Icon"}
onClick={() => props.ChangeSideBarWallpaper(theme.SideBarWallpaper)} alt={"Select Theme"}/>
);
})
return (
<>
{SideBarWallpaperList}
</>
);
}
SideBarModal.jsx
const DraggableDialog = (props) => {
...
return(
<SideBarWallpaperContainer {...props} />
...
);
}
Through props, I get the ChangeSideBarWallpaper method to change the background and color
And the color for the sidebar text I get using useContext it looks like this
SideBarMenu.jsx
export default function SideBarMenu(props) {
const {SideBarWallpaperValue} = React.useContext(CounterContext);
const [SideBarWallpaperTheme,] = SideBarWallpaperValue;
const SideBarWallpaperStyle = SideBarWallpaperTheme;
return(
<Link
style={{ color: SideBarWallpaperStyle && SideBarWallpaperStyle.color,}}>{item.name}
</Link>
);
}
Now when you roughly understand how all the logic works, I want to explain the problem. The problem is that either the sidebar color or the background does not work for me, please pay attention to the method
props.ChangeSideBarWallpaper (theme.SideBarWallpaper)}
When I use theme.SideBarWallpaper as a parameter inside the ChangeSideBarWallpaper method, only the background works for me and not the color, and this is of course logical, then I tried to apply 2 parameters, the color and the background, it looked like this
onClick={() => props.ChangeSideBarWallpaper(theme.SideBarWallpaper, theme.color)}
But in this case, only the background also worked and the only solution that remained for me to transfer the entire value, it looked like this
onClick={() => props.ChangeSideBarWallpaper(theme)}
And then I was surprised now only the sidebar color works for me but the background does not work, You can look at the picture (sorry if I'm so long and boring to explain :) I just want you to understand the problem as clearly as possible)
I decided to check if I get a picture and yes I get
Now that you understand the problem, I will be interested in hearing your advice, thank you for paying attention to my long question.
Before I demonstrate the solution, I would like to say that the problem lies with me, I, as always, introduced myself impatiently and inattentively: (it turns out that I forgot that in the SideBar component I get the SideBarBackground value in short, the code looked like this
const {someValue} = useContext(SideBarContext);
const {SideBarValue, SideBarWallpaperValue} = React.useContext(CounterContext);
const [SideBarTheme] = SideBarValue;
const [SideBarBackground] = SideBarWallpaperValue;
<div style={{backgroundImage: `url(${SideBarBackground})`}}>
...
</div>
Then I got the SideBarWallpaper value
<div style={{backgroundImage: `url(${SideBarBackground.SideBarWallpaper})`}}>
...
</div>
Related
I want to display 2 different DIVs randomly: one or another. So I try to keep a random boolean state variable and do the render depending on it.
Let's try to display BLUE sign in the blue box, and RED sign in the red box, for simplicity.
import React, {useState} from 'react';
const Loader = () => {
const [redLoader, setRedLoader] = useState(Math.random() < 0.5);
const loader = redLoader ?
<div style={{ backgroundColor: "red" }} key="red">
<div>RED</div>
</div>:
<div style={{ backgroundColor: "blue" }} key="blue">
<div>BLUE</div>
</div>
return (
<div className="full-screen-loader">
{loader}
</div>
)
};
export default Loader;
But sometimes I see the BLUE sign in the red box, for example.
just example
So React renders children from another parent.
If I define my random boolean variable out of the Component, nothing changes.
The only thing I can do is set the random redLoader variable inside useEffect, and then this will work.
So how can I define random state variable before the render, not in useEffect?
And why does React behave like this?
React version: 16.13.1
Based on your comments, you should use a useRef instead of useState: this will persist your data but will not cause a rerender.
const redLoader = useRef(Math.random() < 0.5);
// code
const loader = redLoader.current ?
// more code
I am experiencing an issue where the overflow properties of Tailwind are not working with react lists. I am wondering if this is just a simple mistake on my part or if there is a work around that I need to do.
Image to It Not Working
import CoinSummary from './CoinSummary'
const Holdings = ({ coins }) => {
return (
<div className='overflow-auto p-4'>
<h2 className='text-2xl text-center font-bold mt-4'>Holdings</h2>
{coins &&
coins.map((coin, index) => {
return <CoinSummary key={index} coin={coin} />
})}
</div>
)
}
export default Holdings
I want the list of coins to stay inside of the Holdings component and if it overflows, to have a scroll bar instead. However, as you can see in the photo, it doesn't seem to be working like I expected it to.
I was able to figure it out on my own. It was because I did not specify the height to be h-full
I have tried finding the answer to this on StackOverflow and there are some related posts (e.g. React Child Component Not Updating After Parent State Change) but I want to understand why this is not working...
I have a React application that will display a layout of character cards (that is, each card displays a different character). It uses a child component, CharacterBoard, that lays out the CharacterCards, which would be a grandchild component. I pass the characters down from the App to the CharacterBoard as props, and CharacterBoard in turn maps these out the CharacterCards.
The problem is that I want the state of the character to change when I click on one of them. Specifically, I want the revealed field to change. However, even though the state change is reflected in the array of characters in the App (that is, the revealed field changes correctly), and the change is reflected in the array of characters in CharacterBoard, but not in CharacterCard. In fact, my mapping does not seem to be called at all in CharacterBoard when the props change.
Do I need to use something like getDerivedStateFromProps in CharacterBoard and set the state of that component and then use the state to map the values down to CharacterCard? If so, why?
In short (tl;dr), can you pass props on down through the component chain and map them out along the way and still have all changes reflected automatically?
Thanks for any guidance.
If it helps, the render method of my App is
render() {
const {state: {characters}} = this
return (
<div>
<header>
</header>
<main>
<CharacterBoard
onCardSelected={this.onCardSelected}
rowSize={logic.ROW_SIZE}
characters={characters}
cardSize={this.CARD_SIZE}/>
</main>
</div>
);
}
that of CharacterBoard is
render() {
const {props: {characters, rowSize, cardSize,onCardSelected}} = this
const rowUnit = 12 / rowSize
const cardLayout = characters
.map((character, i) => (
<Col xs={6} sm={rowUnit} key={character.name}>
<CharacterCard
onCardSelected = {onCardSelected}
key={i + Math.random()}
character={character}
cardSize={cardSize}
/>
</Col>
)
)
return (
<div>
<Container>
<Row>
{cardLayout}
</Row>
</Container>
</div>
)
}
and finally CharacterCard has this render method
render() {
const {props: {character, cardSize}} = this
const {thumbnail, revealed} = character
const imgURL = `${thumbnail.path}/${cardSize}.${thumbnail.extension}`
const topCardClass = classNames('characterCard__card-back', {'characterCard__card-back--hidden': revealed})
console.log(revealed)
return < a href="/#" onClick={this.onCardSelected}>
<div className='characterCard__card'>
<div className={topCardClass}>
<img src="/images/card_back.png" alt=""/>
</div>
< div className='characterCard__card-front'>< img alt=''
src={imgURL}/>
</div>
</div>
</a>
}
Doh! A simple forgetting to setState in App. Knowing that it should work made me go back through the code one more time and see that, indeed, it was a stupid error on my part.
I'm attempting to do an animation with React and CSS classes. I have created a live demo, if you visit it and click the Start button you will see the text fade in and up one by one. This is the desired animation that I am after.
However, there seems to be issues of consistency when you hit Start multiple times and I cannot pinpoint why.
The Issue: Below is a recording of the issue, you can see the number 1 is not behaving as expected.
live demo
The process: Clicking Start will cancel any previous requestAnimationFrame' and will reset the state to it's initial form. It then calls the showSegments() function with a clean state that has no classNames attached to it.
This function then maps through the state adding a isActive to each segment in the state. We then render out the dom with a map and apply the new state.
This should create a smooth segmented animation as each class gets dropped one by one. However when i test this in Chrome (Version 56.0.2924.87 (64-bit)) and also on iOS, it is very inconsistent, sometimes it works perfectly, other times the first DOM element won't animate, it will just stay in up and visible it's completed transitioned state with "isActive".
I tried to replicate this issue in safari but it worked perfectly fine, I'm quite new to react so i am not sure if this is the best way to go about things, hopefully someone can offer some insight as to why this is behaving quite erratic!
/* MotionText.js */
import React, { Component } from 'react';
import shortid from 'shortid';
class MotionText extends Component {
constructor(props) {
super(props);
this.showSegments = this.showSegments.bind(this);
this.handleClickStart = this.handleClickStart.bind(this);
this.handleClickStop = this.handleClickStop.bind(this);
this.initialState = () => { return {
curIndex: 0,
textSegments: [
...'123456789123456789123456789123456789'
].map(segment => ({
segment,
id: shortid.generate(),
className: null
}))
}};
this.state = this.initialState();
}
handleClickStop() {
cancelAnimationFrame(this.rafId);
}
handleClickStart(){
cancelAnimationFrame(this.rafId);
this.setState(this.initialState(), () => {
this.rafId = requestAnimationFrame(this.showSegments);
});
}
showSegments() {
this.rafId = requestAnimationFrame(this.showSegments);
const newState = Object.assign({}, this.state);
newState.textSegments[this.state.curIndex].className = 'isActive';
this.setState(
{
...newState,
curIndex: this.state.curIndex + 1
},
() => {
if (this.state.curIndex >= this.state.textSegments.length) {
cancelAnimationFrame(this.rafId);
}
}
);
}
render(){
const innerTree = this.state.textSegments.map((obj, key) => (
<span key={obj.id} className={obj.className}>{obj.segment}</span>
));
return (
<div>
<button onClick={this.handleClickStart}>Start</button>
<button onClick={this.handleClickStop}>Stop</button>
<hr />
<div className="MotionText">{innerTree}..</div>
</div>
)
}
}
export default MotionText;
Thank you for your time, If there any questions please ask
WebpackBin Demo
Changing the method to something like this works
render(){
let d = new Date();
const innerTree = this.state.textSegments.map((obj, key) => (
<span key={d.getMilliseconds() + obj.id} className={obj.className}>{obj.segment}</span>
));
return (
<div>
<button onClick={this.handleClickStart}>Start</button>
<button onClick={this.handleClickStop}>Stop</button>
<hr />
<div className="MotionText">{innerTree}..</div>
</div>
)
}
How this helps is that, the key becomes different than previously assigned key to first span being rendered. Any way by which you can make the key different than previous will help you have this animation. Otherwise React will not render it again and hence you will never see this in animation.
I have a sub component that does not need to be loaded immediately that I want to split out. I am trying to conditionally load in a react component via require.ensure. I am not getting any console errors but I am also not seeing anything being loaded. Here is the code I am calling :
renderContentzones() {
if (this.props.display ) {
return require.ensure([], () => {
const Component = require('./content-zones/component.jsx').default;
return (
<Component
content={this.props.display}
/>
);
});
}
return null;
}
It is just rendering a blank screen currently (no errors). This previously worked when I used import 'displayComponent' from './content-zones/component.jsx' and just returned it like you normally would in react, instead of this require.ensure but. Not sure what I am doing wrong here, any idea how to make something like this work? Thanks!
This is one way to do it, using the state to show the dynamic loaded component:
constructor(){
this.state = {cmp:null};
}
addComponent() {
const ctx = this;
require.ensure(['../ZonesComponent'], function (require) {
const ZonesComponent = require('../ZonesComponent').default;
ctx.setState({cmp:<ZonesComponent />});
});
}
render(){
return (
<div>
<div>Some info</div>
<div><button onClick={this.addComponent.bind(this)}>Add</button></div>
<div>
{this.state.cmp}
</div>
</div>
);
}
When you press the button add the component will be shown.
Hope this help.