I'm trying to use a color as a prop in a reactjs setting. In the General_heading component, I want to be able to pass a prop to define the color of the heading, but I can't get it to respond currently. Thoughts?
in app.js:
<div><General_header theme_color="red"/></div>
in General_header.js:
import React, { Component } from 'react';
class General_header extends React.Component {
constructor(props) {
super(props);
this.state = {logo_img: props.logo_img,
theme_color: props.theme_color};
}
render() {
return (
<div style={[styles.header,{backgroundColor:this.state.theme_color}]}>test
<img src={this.state.logo_img} alt={'logo'} style={styles.logo_img} />
</div>
);
}
}
var styles = {
header: {
height: '100px',
display: 'block'},
logo_img: {
height: '40px',
display: 'inline-block'}
}
export default General_header;
Use camelCase
Have a look at this https://github.com/airbnb/javascript/tree/master/react#naming
<GeneralHeader themeColor="red"/>
constructor(props) {
super(props);
this.state = {
logoImg: props.logoImg,
themeColor: props.themeColor
};
}
<div style={{backgroundColor: this.state.themeColor}}>
Made some changes below to your code:
class General_header extends React.Component {
render() {
// This will create a single object containing fields
// from styles.header plus the extra backgroundColor field.
const fullStyle = {
...styles.header, // This is called the spread operator
backgroundColor: this.props.theme_color // No need to assign this to state
}
return (
<div style={fullStyle}>test
<img src={this.state.logo_img} alt={'logo'} style={styles.logo_img} />
</div>
);
}
}
ES5 version:
var fullStyle = Object.assign(
{},
styles.header,
{ backgroundColor: this.props.theme_color }
);
Related
I'm trying to add pre-loader icon before rendering an image in React component using the below code:
export default class MediaPostItem extends BaseComponent {
public constructor(props: IMediaPostItemProperties) {
super(props);
this.state = {
imageIsReady: false,
};
}
componentDidMount(): void {
const img = new Image();
img.onload = () => this.setState({ imageIsReady: true });
img.src = this.props.item.featuredImage?.node.sourceUrl;
}
public render(): ReactNode {
const { item } = this.props;
const { imageIsReady } = this.state;
return(
{imageIsReady ? (
<img src={item.featuredImage?.node.sourceUrl}/>
) : (<div> Loading</div>)
})
}
}
Now in the other component I give a HTML strings as a real HTML in a react component using the below code:
<div dangerouslySetInnerHTML={{ __html: props.content }}></div>
The content is including some <img> tags and what I need is to add a pre-loader for these img tags too.
I'm not sure if it's possible or not. Is there any way to apply changes to the HTML strings when it comes from props?
You can do that with ReactDOM.render.
import { render } from "react-dom";
const htmlString = `
<div>
<div id="toBeReplaced"/></div>
</div>
`;
class ReplacedComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>Replaced Component</h1>;
}
}
export default class Parent extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
render(
<React.StrictMode>
<ReplacedComponent />
</React.StrictMode>,
document.getElementById("toBeReplaced")
);
}
render() {
return <div dangerouslySetInnerHTML={{ __html: htmlString }}></div>;
}
}
You would need to select the <img> tags, with some attribute (class/id) then loop over them and render your component.
App.js
import React from 'react';
import './App.css'
import Tools from './components/class/Tools'
import Loading from './components/inc/Loading'
export default class App extends React.Component {
componentDidMount() {
Tools.showLoading(); // or new Tools();
}
render() {
return (
<div className="App">
<Loading />
</div>
)
}
}
Loading.js:
import React from 'react'
export default class Loading extends React.Component {
constructor(props) {
super(props)
this.state = {
display: 'none'
}
}
render() {
return (
<div className="loading" style={{display: this.state.display}}>
<span></span>
</div>
)
}
}
Tools.js
export default class Tools extends React.Component {
static showLoading(){ // or non-static
Loading.setState ...
}
}
I want change display state from outside of Loading component.
I use Loading in whole my project and I want create function for handle it.
Example for another use:
function xxx(){
Tools.showLoading(); // or new Tools();
}
Or:
<span onClick={Tools.showLoading(); // or new Tools();}></span>
Actually, I want create only one function to manage and handle display of Loading.
In Tools.js
let loadingStateSetter = null
export function setLoadingStateSetter(setter) {
loadingStateSetter = setter
return () => loadingStateSetter = null
}
export function setLoadingState(value) {
if (loadingStateSetter !== null) loadingStateSetter(value)
}
In Loading.js:
import { setLoadingStateSetter } from './Tools.js'
export default class Loading extends React.Component {
constructor(props) {
super(props)
this.state = {
display: 'none'
}
}
render() {
return (
<div className="loading" style={{display: this.state.display}}>
<span></span>
</div>
)
}
componentDidMount() {
this.removeStateSetter = setLoadStateSetter((value) => {
this.setState((state) => ({
...state,
display: value,
})
})
}
componentWillUnmount() {
this.removeStateSetter()
}
}
Usage:
import { setLoadingState } from './Tools.js'
function xxx(){
setLoadingState('some value')
}
While you can easily expose a setState function externally, it acts just like any other function, its not usually a good idea. You should instead consider rewriting your Loading component to use the property object to tell it if its loading and track the loading state higher up the component tree where it is accessible by things that would want to change its status.
I think you can using redux as store manager global state
https://redux.js.org/
another way pass it through props and handle it at parent component
I'm building an app with React, and getting a TypeError that my function doesn't exist. My function starts in here where I pass it to a child component:
class InvoiceScreen extends Component {
state = {
numberOfInvoices: InvoiceData.length,
currentDisplay: <InvoiceList
openInvoice={this.openInvoice}
/>
};
checkInvoiceLength = () => {
var isEmpty = document.getElementById("InvoiceList").innerHTML === "";
if (isEmpty == false) {
this.setState({display: "untoggled"})
}
else if (isEmpty == true) {
this.setState({hasInvoices: "toggled"})
}
}
openInvoice = (int) => {
this.setState({currentDisplay:
<InvoiceDetails
idNumber={InvoiceData[int].id}
description={InvoiceData[int].description}
street={InvoiceData[int].senderAddress.street}
city={InvoiceData[int].senderAddress.city}
postCode={InvoiceData[int].senderAddress.postCode}
country={InvoiceData[int].senderAddress.country}
createdAt={InvoiceData[int].createdAt}
paymentDue={InvoiceData[int].paymentDue}
clientStreet={InvoiceData[int].clientAddress.street}
clientCity={InvoiceData[int].clientAddress.city}
clientPostCode={InvoiceData[int].clientAddress.postCode}
clientCountry={InvoiceData[int].clientAddress.country}
clientEmail={InvoiceData[int].clientEmail}
items={InvoiceData[int].items}
total={InvoiceData[int].total}
/>})
}
render() {
return(
<div className="InvoiceScreen">
<IconBar />
<div className="DisplayArea">
{this.state.currentDisplay}
</div>
</div>
)
}
}
Then from this component, I pass it down to multiple components created through the map function
class InvoiceList extends Component {
constructor(props) {
super(props)
}
render() {
return(
<div className="InvoiceListScreen">
<InvoiceOptions numberOfInvoices={this.props.numberOfInvoices} />
<div id="InvoiceList">
{InvoiceData.map((invoice, index,) =>
<InvoiceBar
openInvoice={this.props.openInvoice}
key={index}
position={index}
idNumber={invoice.id}
clientName={invoice.clientName}
paymentDue={invoice.paymentDue}
price={Formatter.format(invoice.total)}
status={invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1).toLowerCase()}
/>
)}
</div>
</div>
)
}
}
And then finally inside of the mapped components, I call it as an onClick
class InvoiceBar extends Component {
constructor(props) {
super(props)
}
render() {
return(
<div className="InvoiceBar" onClick={() => this.props.openInvoice(this.props.position)}>
<h4 className="idNumber"><span className="Hashtag">#</span>{this.props.idNumber}</h4>
<p className="clientName">{this.props.clientName}</p>
<div className="DueAndPrice">
<p className="paymentDue">Due {this.props.paymentDue}</p>
<h3 className="price">{this.props.price}</h3>
</div>
<PaymentStatus status={this.props.status} />
</div>
)
}
}
And then like I said, I'm given a TypeError saying that it isn't a function. I'm wondering if it has something to do with the function being passed as props from outside of the map function in the second component. Can someone please enlighten me on what it is I'm doing wrong?
The problem is that class fields run in order in which they're listed. They're not like normal methods, which get defined on the prototype ahead of time. For a simplified version:
class InvoiceScreen extends Component {
state = {
openInvoice: this.openInvoice
};
openInvoice = () => {
// some function
}
desugars to:
class InvoiceScreen extends Component {
constructor(props) {
super(props);
this.state = {
openInvoice: this.openInvoice
};
this.openInvoice = () => {
// some function
}
See the problem? You're defining this.state before you're defining this.openInvoice.
Easiest solution would be to move the definition of this.state to the bottom:
class InvoiceScreen extends Component {
// PUT ALL OTHER METHOD DEFINITIONS HERE
// then just before the end of the component:
state = {
numberOfInvoices: InvoiceData.length,
currentDisplay: <InvoiceList
openInvoice={this.openInvoice}
/>
};
}
That said, putting a React component into state is really, really weird. Consider a different approach if at all possible.
I have two commponents. Map and LfMap.
import React, { Component } from 'react';
import { LfMap } from '../../components/Localisation/LfMap';
export class Map extends Component {
constructor(props) {
super(props);
this.state = {
dev: [],
};
}
componentDidMount() {
fetch("URL")
.then(resp => resp.json())
.then(pas => this.setState({dev: pas}));
}
render() {
return(
<React.Fragment>
<LfMap list={this.state.dev}/>
</React.Fragment>
);
}
}
import React from 'react';
import { Map, CircleMarker, TileLayer, Polyline} from "react-leaflet";
import "leaflet/dist/leaflet.css";
import Control from 'react-leaflet-control';
class LfMap extends React.Component {
constructor(props) {
super(props);
this.state = {
devices: this.props.list,
}
}
render() {
return(
<React.Fragment>
<Map
style={{ height: "480px", width: "100%" }}
zoom={12}
center={[22.22, 21.00]}
>
<TileLayer url="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
</Map>
</React.Fragment>
);
}
}
export { LfMap };
And I'm passing a prop list to LfMap with list of my objects.
But when I'm trying to console.log(this.state.devices) it shows Undefined but it should console my list of objets which i fetched in other component. Why it's happening?
Why set state = props ? is an anti-pattern, you should keep using this.prop.list in the other component.
Do this:
console.log(this.props.list)
it will print [], and then when the results come it will print the full array.
When you need to use this array always use this.props.list.
Based in your comment here is how you give solution to that:
At the parent you add a function
listUpdate(newList){
this.setState({
list: newList
})
}
and then you pass this function to the child
<LfMap list={this.state.dev} listUpdate={this.listUpdate}/>
when you need to update it you need to call this.props.listUpdate.
Always update the props :) that's where the reference is, this is the pattern you should follow.
import React from "react";
import ReactDOM from "react-dom";
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
let child = this.props.children[0];
/** Always returns as undefined */
//if (typeof child.childMethod == "function") {
// child.childMethod();
//}
/**
* EDIT: Close, however the this binding seems to not be working. I can however provide the childs props to the childMethod and work with that.
*/
if(typeof child.type.prototype.childMethod == "funciton"){
child.type.prototype.childMethod();
}
}
render() {
return (
<div>
{this.props.children}
<button onClick={this.runMethod}>run</button>
</div>
);
}
}
const App = ({}) => {
return (
<div>
<WrappingComponent>
<NestedComponent />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
So the goal is to have optional methods attached to a nested component that can execute from the wrapping component, almost like an event emmiter. For some reason though, the method that exists on the child component claims not to exist. However whenever I log the child component pulled from the array of the this.props.children the prototype has the method listed.
Am I missing a special way to access methods of children components through a methods variable perhaps?
Found the variable I can use to access it. If anyone has any more insight into this, or reasons why what I am doing is poor practice please let me know.
Editing the question where this is needed, but the item below is accessing the function of the child:
child.type.prototype.childMethod
Does not appear to maintain the this binding. Passing props down does work however.
You should manage all of this logic in the top level component (the App component)
class NestedComponent extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method one ran");
}
render() {
return <div>NestedComponent</div>;
}
}
class NestedComponentTwo extends React.Component {
constructor(props) {
super(props);
this.childMethod = this.childMethod.bind(this);
}
childMethod() {
alert("Child method two ran");
}
render() {
return <div>NestedComponentTwo</div>;
}
}
class WrappingComponent extends React.Component {
render() {
return (
<div>
{this.props.children}
<button onClick={this.props.onClick}>run</button>
</div>
);
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.runMethod = this.runMethod.bind(this);
}
runMethod() {
if (this.nestedComponent) {
this.nestedComponent.childMethod();
}
}
render() {
return (
<div>
<WrappingComponent onClick={this.runMethod}>
<NestedComponent ref={el => this.nestedComponent = el} />
<NestedComponentTwo />
</WrappingComponent>
</div>
);
}
};
if (document.getElementById("example")) {
ReactDOM.render(<App />, document.getElementById("example"));
}
<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="example"></div>
Moreover ref with string attribute is deprecated https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs