Managing state when changing backgroundImage - reactjs

In state I'm setting background image with a photo and I wanted to do 2 buttons, one is changing background image to another photo and another is setting background image back to the first photo.
Here is piece of my code:
\\index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import "semantic-ui/dist/semantic.min.css";
import logo1 from "./modules/images/one.jpg";
ReactDOM.render(
<App bgImage={`url(${logo1})`} />,
document.getElementById("root")
);
\\App.js
import React, { Component } from "react";
import "./App.css";
import NavBar from "./modules/NavBar";
import logo1 from "./modules/images/one.jpg";
import logo2 from "./modules/images/night.jpg";
class App extends Component {
constructor(props) {
super(props);
this.state = {
bgImage: props.bgImage //so here I set the backroundImage with logo1, because I want to logo1 to be on the start of application, but when I change to logo2 I want to have that logo2 even when I refresh page ( but when I do it, constructor of App is setting it to the logo1. So maybe, can I save that logo even when the App is reloading to get the latest logo ?
};
}
ChangeToLightMode = e => {
this.setState({
bgImage: `url(${logo1})`
});
};
ChangeToDarkMode = e => {
this.setState({
bgImage: `url(${logo2})`
});
};
render() {
return (
<div
style={{
display: "flex",
minHeight: "100vh",
flexDirection: "column",
backgroundImage: this.state.bgImage,
height: "100%",
width: "100%"
}}
>
<NavBar
ChangeToDarkMode={this.ChangeToDarkMode}
ChangeToLightMode={this.ChangeToLightMode}
/>
);
}
}
export default App;
\\NavBar.js
import React, { Component } from "react";
class NavBar extends Component {
render() {
return (
<div>
<Menu fixed="top" inverted>
<Menu.Menu position="right">
<Menu.Item onClick={this.props.ChangeToDarkMode}>
DarkMode
</Menu.Item>
<Menu.Item onClick={this.props.ChangeToLightMode}>
LightMode
</Menu.Item>
</Menu.Menu>
</Menu>
</div>
);
}
}
So I implemented a way that I can change background photo but when I click for example logo in my application and the constructor of App is called it is setting my bgImage to the first photo even when I had second photo and I know it. But I want only the first photo to be set in constructor in the start of the application, after it I want to have the photo according to which method I use. So should I store somewhere the bgImage state and do something with it in App constructor ?

If the default value for bgImage needs to be determined before App is rendered, then usually you would do this by passing a prop into the component e.g.
<App bgImage={`url(${logo1})`} />
Then in the constructor, you can set this as the default state
class App extends Component {
constructor(props) {
super(props);
this.state = {
bgImage: props.bgImage
};
}
}

Related

Display element based on event fired and props passed in

I am trying, to manipulate another element, by, passing props directly to it, and then have it display itself. If I pass true/false.
Live running code:
https://codesandbox.io/s/keen-dan-rt0kj
I don't know if it's possible to have a system of objects, and based on an event, tell a parent to display a child.
App.js
import React from "react";
import "./styles.css";
import Content from "./components/Content";
export default class App extends React.Component {
state = {
display: false
};
render() {
return (
<div className="App">
<button onClick={() => this.setState({ display: !this.state.display })}>
Display div
</button>
<Content display={this.state.display} />
</div>
);
}
}
./components/Content.js:
import React from "react";
export default class Content extends React.Component {
constructor(props) {
super();
this.state = {
display: props.display
};
}
render() {
const { display } = this.state;
return (
<div
id="mydiv"
className="mydiv"
style={{ display: display ? "block" : "none" }}
>
<h3>A simple div</h3>
</div>
);
}
}
Goal:
I want to based on a state, and based on fired event, display an element that already in store of root.
EDIT: I am aware that, this exists and can be used: import PropTypes from 'prop-types', however, I am not sure this is good practice, since it requires some parent or some other component to implement the props.
JUST Tried:
App:
<Content display={this.state.display} content={"Hello World"} />
Content:
<h3>{this.state.content}</h3>
It seems the passed in text, stored in Content state = {content: props.content} does get displayed, wheres, the boolean value does not work directly. Is there something wrong with sending in a bool ?
try this in your Content Component
export default class Content extends React.Component {
constructor(props) {
super();
this.state = {
};
}
render() {
return (
<>
{this.props.display?(
<div
id="mydiv"
className="mydiv"
>
<h3>A simple div</h3>
</div>
):null}
</>
);
}
}
The reason this may not be working is because you are initiating the state in a way that does not connect the display props after the component is initialized. This means that after the Content component is "constructed", the state of the Content and it's parent are not linked. This is because the constructor() function is only run once to initialize the state.
The best option you have is to not use the internal state of the Content component. Rather than initializing state with the display prop, just use the display prop in your render function.
Trying something like this might work
import React from "react";
export default class Content extends React.Component {
constructor(props) {
super(props);
}
render() {
const { display } = this.props;
return (
<div
id="mydiv"
className="mydiv"
style={{ display: display ? "block" : "none" }}
>
<h3>A simple div</h3>
</div>
);
}
}
Also I would reccommend using state in the root:
import React from "react";
import "./styles.css";
import Content from "./components/Content";
export default class App extends React.Component {
constructor(props) {
super();
state = {
display: false
};
}
render() {
return (
<div className="App">
<button onClick={() => this.setState({ display: !this.state.display })}>
Display div
</button>
<Content display={this.state.display} />
</div>
);
}
}

How to scroll to bottom of div

I have troubles with scrolling down my component. I want scroll bar thumb to be at the very end of chat div from the start. I am not sure what the probleme is here. MessageTo and MessageFrom are components that represent paragraphs with text and display author's name. ChatInput is textarea with send button.
import React from 'react';
import './index.css';
import MessageFrom from './message-from';
import MessageTo from './message-to';
import ChatInput from './chat-input';
import SimpleBarReact from 'simplebar-react';
import 'simplebar/src/simplebar.css';
class Chat extends React.Component {
componentDidMount() {
this.scrollToBottom();
}
componentDidUpdate(){
this.scrollToBottom()
}
scrollToBottom = () => {
this.el.scrollIntoView({behavior:"smooth"});
}
render(){
return(
<div className="chat" id="slider-container">
<SimpleBarReact style={{maxHeight: 680}} id="scroll-bar-cont">
<div className="messages-container" id="for-slider" >
<MessageFrom />
<MessageTo />
<div style={{ float:"left", clear: "both" }}
ref={el => {this.el=el;}}>
</div>
</div>
</SimpleBarReact>
<ChatInput />
</div>
);
}
}
export default Chat;
https://codesandbox.io/s/great-currying-x4m8m?file=/src/message-to.js
It works well. But It doesn't work in next level component.

withStyles component wrap

I have a following component:
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '#material-ui/core/styles';
import SnackbarContent from '#material-ui/core/SnackbarContent';
import Snackbar from '#material-ui/core/Snackbar';
const styles = theme => ({
error: {
backgroundColor: theme.palette.error.dark,
}
})
class Snack extends React.Component {
state = {
opendialog: false,
}
constructor(props) {
super(props);
}
test() {
this.setState({opendialog: !this.state.opendialog});
}
render() {
return (
<Snackbar open={this.state.opendialog}>
<SnackbarContent message="test"/>
</Snackbar>
);
}
}
export default withStyles(styles)(Snack);
and app main:
import React, { Component } from 'react';
import Button from '#material-ui/core/Button';
import logo from './logo.svg';
import './App.css';
import Snack from './Snack.js';
class App extends Component {
constructor(props) {
super(props);
this.snack = React.createRef();
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<Button variant="contained" color="primary" onClick={this.handleHello}>Hello World</Button>
<div>
<Snack ref={ ref => this.snack = ref } />
</div>
</div>
);
}
handleHello = () => {
this.snack.test();
}
}
export default App;
I get a "TypeError: _this.snack.test is not a function" when I click the button, however if I drop the withStyles the code works correctly.
I'm just replacing "export default withStyles(styles)(Snack);" line with "export default (Snack);".
Why it does not work correctly with the "withStyles"? How can I make it work?
Because withStyles wraps your component, you need to instead use:
<Snack innerRef={ref => (this.snack = ref)} />
withStyles passes the innerRef property to the wrapped component as ref.
I tried this using the latest version of #material-ui/core (currently 3.8.1). I can't guarantee that older versions support this in the same way.
Here's a fully working example:
The problem is because the withStyles HOC return a new component so you are getting the reference of the HOC. You can use innerRef prop:
<Snack innerRef={ ref => this.snack = ref } />
According to the official documentation:
It adds an innerRef property so you can get a reference to the wrapped component. The usage of innerRef is identical to ref.
You can check it in the official documentation here withStyle function.
I already tested it with your current version it works properly

ReactComponent Button value won't render on my react app

I've got a simple React App going on. My index.js file looks, of course, like this:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
registerServiceWorker();
Going deeper, my App.js file declares an App extends Compoennt class, which contains my to-be-rendered elements and their functions:
import React, { Component } from "react";
import logo from "./SmartTransit_logo.png";
import MyButton from "./components/MyButton";
import "./App.css";
import { isWallet, helloWorld } from "./services/neo-service";
class App extends Component {
state = {
inputValue: ""
};
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Smart Transit Live Demo</h1>
</header>
<div style={{ width: 500, margin: "auto", marginTop: 10 }}>
<MyButton
buttonText="My Button"
onClick={ params => {helloWorld();}}
/>
</div>
</div>
);
}
}
export default App;
And the declaration of MyButton from /components/MyButton:
import React, { Component } from "react";
import PropTypes from "prop-types";
class MyButton extends Component {
render() {
return (
<button className="MyButton"
value = {this.props.buttonText}
>
{this.props.children}
</button>
);
}
}
MyButton.propTypes = {
buttonText: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
};
export default MyButton;
Finally, the declaration for helloWorld() is done like so (NOTE: neon-js is an npm package I'm using):
import { wallet } from "#cityofzion/neon-js";
export function isWallet(address) {
console.log(wallet.isAddress(address));
return wallet.isAddress(address);
}
export function helloWorld() {
console.log("Hello world");
return 1;
}
My problem is that the resulting Button doesn't get its value text rendered, and although it gets the CSS code for it just fine, it appears empty!
Not only that, but pressing it doesn't log a "Hello World" in the console, as it should, so it's even disconnected from its onClick function.
Any idea on what I'm doing wrong?
Buttons don't receive a "value" prop. The text inside of the button element is what gives it its text.
The button does appear to accept children to use as button text, but no children is actually being passed down to it. this.props.children is the content between JSX tags when the component is rendered.
React doesn't add the event handlers to elements automatically. You have to pass them along yourself in order for them to be properly triggered.
With that in mind, here's how you should render your button in App:
<MyButton onClick={() => helloWorld()}>
My Button
</MyButton>
And here's how MyButton's code should look:
class MyButton extends Component {
render() {
return (
<button className="MyButton" onClick={this.props.onClick}>
{this.props.children}
</button>
)
}
}
As you can see, the buttonText prop is no longer required; that's what the children prop is for.
You need to define super(props) in class constructor when you are going to use this.props
constructor(props) {
super(props);
}
Define this in MyButton component.
The problem is, you are not calling onClick method from mybutton component and button take it's value between it's opening and closing tag.
Use this code:
this.props.onClick()}> {this.props.buttonText}

Watching state from child component React with Material UI

New to React. Just using create-react-app and Material UI, nothing else.
Coming from an Angular background.
I cannot communicate from a sibling component to open the sidebar.
I'm separating each part into their own files.
I can get the open button in the Header to talk to the parent App, but cannot get the parent App to communicate with the child LeftSidebar.
Header Component
import React, {Component} from 'react';
import AppBar from 'material-ui/AppBar';
import IconButton from 'material-ui/IconButton';
import NavigationMenu from 'material-ui/svg-icons/navigation/menu';
class Header extends Component {
openLeftBar = () => {
// calls parent method
this.props.onOpenLeftBar();
}
render() {
return (
<AppBar iconElementLeft={
<IconButton onClick={this.openLeftBar}>
<NavigationMenu />
</IconButton>
}
/>
);
}
}
export default Header;
App Component -- receives event from Header, but unsure how to pass dynamic 'watcher' down to LeftSidebar Component
import React, { Component } from 'react';
import darkBaseTheme from 'material-ui/styles/baseThemes/darkBaseTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import RaisedButton from 'material-ui/RaisedButton';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
// components
import Header from './Header/Header';
import Body from './Body/Body';
import Footer from './Footer/Footer';
import LeftSidebar from './LeftSidebar/LeftSidebar';
class App extends Component {
constructor() {
super() // gives component context of this instead of parent this
this.state = {
leftBarOpen : false
}
}
notifyOpen = () => {
console.log('opened') // works
this.setState({leftBarOpen: true});
/*** need to pass down to child component and $watch somehow... ***/
}
render() {
return (
<MuiThemeProvider muiTheme={getMuiTheme(darkBaseTheme)}>
<div className="App">
<Header onOpenLeftBar={this.notifyOpen} />
<Body />
<LeftSidebar listenForOpen={this.state.leftBarOpen} />
<Footer />
</div>
</MuiThemeProvider>
);
}
}
export default App;
LeftSidebar Component - cannot get it to listen to parent App component - Angular would use $scope.$watch or $onChanges
// LeftSidebar
import React, { Component } from 'react';
import Drawer from 'material-ui/Drawer';
import MenuItem from 'material-ui/MenuItem';
import IconButton from 'material-ui/IconButton';
import NavigationClose from 'material-ui/svg-icons/navigation/close';
class LeftNavBar extends Component {
/** unsure if necessary here **/
constructor(props, state) {
super(props, state)
this.state = {
leftBarOpen : this.props.leftBarOpen
}
}
/** closing functionality works **/
close = () => {
this.setState({leftBarOpen: false});
}
render() {
return (
<Drawer open={this.state.leftBarOpen}>
<IconButton onClick={this.close}>
<NavigationClose />
</IconButton>
<MenuItem>Menu Item</MenuItem>
<MenuItem>Menu Item 2</MenuItem>
</Drawer>
);
}
}
export default LeftSidebar;
Free your mind of concepts like "watchers". In React there is only state and props. When a component's state changes via this.setState(..) it will update all of its children in render.
Your code is suffering from a typical anti-pattern of duplicating state. If both the header and the sibling components want to access or update the same piece of state, then they belong in a common ancestor (App, in your case) and no where else.
(some stuff removed / renamed for brevity)
class App extends Component {
// don't need `constructor` can just apply initial state here
state = { leftBarOpen: false }
// probably want 'toggle', but for demo purposes, have two methods
open = () => {
this.setState({ leftBarOpen: true })
}
close = () => {
this.setState({ leftBarOpen: false })
}
render() {
return (
<div className="App">
<Header onOpenLeftBar={this.open} />
<LeftSidebar
closeLeftBar={this.close}
leftBarOpen={this.state.leftBarOpen}
/>
</div>
)
}
}
Now Header and LeftSidebar do not need to be classes at all, and simply react to props, and call prop functions.
const LeftSideBar = props => (
<Drawer open={props.leftBarOpen}>
<IconButton onClick={props.closeLeftBar}>
<NavigationClose />
</IconButton>
</Drawer>
)
Now anytime the state in App changes, no matter who initiated the change, your LeftSideBar will react appropriately since it only knows the most recent props
Once you set the leftBarOpen prop as internal state of LeftNavBar you can't modify it externally anymore as you only read the prop in the constructor which only run once when the component initialize it self.
You can use the componentWillReceiveProps life cycle method and update the state respectively when a new prop is received.
That being said, i don't think a Drawer should be responsible for being closed or opened, but should be responsible on how it looks or what it does when its closed or opened.
A drawer can't close or open it self, same as a light-Ball can't turn it self on or off but a switch / button can and should.
Here is a small example to illustrate my point:
const LightBall = ({ on }) => {
return (
<div>{`The light is ${on ? 'On' : 'Off'}`}</div>
);
}
const MySwitch = ({ onClick, on }) => {
return (
<button onClick={onClick}>{`Turn the light ${!on ? 'On' : 'Off'}`}</button>
)
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
lightOn: false
};
}
toggleLight = () => this.setState({ lightOn: !this.state.lightOn });
render() {
const { lightOn } = this.state;
return (
<div>
<MySwitch onClick={this.toggleLight} on={lightOn} />
<LightBall on={lightOn} />
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Resources