I am using React JSX. I have a div with className="shadow" as shown below.
<div className="main">
<div className="shadow" style={{backgroundColor: "#FFFFFF"}}>
<div id="wrapper">
Hello
</div>
</div>
</div>
Based on a certain condition being true or false, I want to remove the div with className="shadow", but want to keep every div including the div with id="wrapper" intact. Something like unwrap() method of jQuery.
Something to the effect of what is written below, but without so many lines of code.
if ( currentPage==="login") {
<div className="main">
<div id="wrapper">
Hello
</div>
</div>
}
else {
<div className="main">
<div className="shadow" style={{backgroundColor: "#FFFFFF"}}>
<div id="wrapper">
Hello
</div>
</div>
</div>
}
I checked React.js: Wrapping one component into another and How to pass in a react component into another react component to transclude the first component's content?, but didn't get what I am looking for.
Maybe what can help you is to change the className based on the page you are on, because what you try to do would be better using react-router to display different components based on the path you are on.
Use something similar to this code, I hope I can help you.
const App = React.createClass({
changePage(nextPage) {
this.setState({ page: nextPage })
},
getInitialState() {
return({
page: 'Login'
})
},
render() {
return(
<div className="main">
<div className={ this.state.page === 'Login' ? 'shadow' : '' }>
<div id="wrapper">
Hello from { this.state.page } page.
</div>
<button onClick={ this.changePage.bind(null, 'Login') }>Go to Login page.</button>
<button onClick={ this.changePage.bind(null, 'Home') }>Go to Home page.</button>
</div>
</div>
)
}
})
ReactDOM.render(<App />, document.getElementById('app'))
div.shadow{ background-color: #000000; color: #ffffff}
<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='app'></div>
This is a great use case for Higher Order Components!
const HOC = (currentPage, Shadow, Wrapper) => (
() => <div className="main">
{
currentPage === 'login'
? <Shadow {...this.props} />
: <Shadow {...this.props}><Wrapper {...this.props}>{this.props.children}</Wrapper></Shadow>
}
</div>
)
Usage:
render () {
const Shadow = props => <div className="shadow" style={{backgroundColor: '#FFFFFF'}}>{props.children}</div>
const Wrapper = props => <div id="wrapper">Hello</div>
const Example = HOC(
currentPage,
Shadow,
Wrapper
)
return (
<Example />
)
}
Update:
To render the children of <Wrapper />, use {this.props.children} and use the class syntax in HOC:
const HOC = (currentPage, Shadow, Wrapper) => (
class extends Component {
render () {
return (
<div className="main">
{
currentPage === 'login'
? <Shadow />
: <Shadow><Wrapper>{this.props.children}</Wrapper></Shadow>
}
</div>
)
}
}
)
If you needed to capture props on <Shadow /> and <Wrapper />, then do something like the following. Note: I don't think you can pass props into normal DOM elements like <div> tags. But if they were other components with a Capital starting letter, then I believe passing props through with {...this.props} would work nicely.
const HOC = (currentPage, Shadow, Wrapper) => (
class extends Component {
render () {
return (
<div className="main">
{
currentPage === 'login'
? <Shadow {...this.props} />
: <Shadow {...this.props}><Wrapper {...this.props}>{this.props.children}</Wrapper></Shadow>
}
</div>
)
}
}
)
Related
I have two React components
class App extends React.Component {
render() {
return (
<div id="appWrapper">
<ConfigureWindow />
<button id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
}
const ConfigureWindow = () => (
<div id="configureWindowWrapper">
<div id="configureWindow">
<section id="addCitySection">TODO: adding a city</section>
<div id="verticalLine"></div>
<section id="listOfCities">
<header>
<h1>Available cities</h1>
<div id="closeConfigureWindowWrapper">
<img src="..\src\images\exit.png" id="closeConfigureWindow" alt="" />
</div>
</header>
<section id="availableCities"></section>
</section>
</div>
</div>
);
I want "ConfigureWindow" to be shown when "configureClocksButton". I tried to execute it with props, state and a function but got errors. It also would be nice if you explain me how to create new React components with React functions?
You probably want to use the React.JS event onClick (https://reactjs.org/docs/handling-events.html), and a state to store the action. To create a function component, you just have to return the JSX you want to render, and use hooks (https://reactjs.org/docs/hooks-intro.html) and then do a conditional rendering (https://reactjs.org/docs/conditional-rendering.html):
const App = () => {
const [toggleConfiguration, setToggleConfiguration] = useState(false)
return (
<div id="appWrapper">
{toggleConfiguration && <ConfigureWindow />}
<button onClick{() => setToggleConfiguration(true)} id="configureClocksButton">Configure clocks</button>
<section id="clocksHere"></section>
</div>
);
}
It's a bit difficult to understand your post, but I gather you want to click the button with id="configureClocksButton" and conditionally render the ConfigureWindow component.
You can accomplish this with some boolean state, a click handler to toggle the state, and some conditional rendering.
class App extends React.Component {
this.state = {
showConfigureWindow: false,
}
toggleShowConfigureWindow = () => this.setState(prevState => ({
showConfigureWindow: !prevState.showConfigureWindow,
}))
render() {
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={this.toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
}
A function component equivalent:
const App = () => {
const [showConfigureWindow, setShowConfigureWindow] = React.useState(false);
const toggleShowConfigureWindow = () => setShowConfigureWindow(show => !show);
return (
<div id="appWrapper">
{showConfigureWindow && <ConfigureWindow />}
<button
id="configureClocksButton"
onClick={toggleShowConfigureWindow}
>
Configure clocks
</button>
<section id="clocksHere"></section>
</div>
);
}
I'm trying to refactor the following component so I can separate the outer / inner components.
// outer component
<Draggable>
{(provided) => (
<div
className={`${styles.listWrapper} ${scrollbarClassName}`}
ref={provided.innerRef}
{...provided.draggableProps}
>
// This is the inner component I'd like to extract:
<div className={`${styles.column} ${scrollbarClassName}`}>
// However I'm using the provided props here:
<div {...provided.dragHandleProps}>
<ColumnHeader {...columnHeaderProps} />
</div>
</div>
</div>
)}
</Draggable>
I'd like to be able to use it this way or similar, but I am not sure how to pass the props through children so I can use the provided prop in my inner component inside DraggableColumn:
const Draggable = <DraggableContainer>
{(provided) => (
<div
className={`${styles.listWrapper} ${scrollbarClassName}`}
ref={provided.innerRef}
{...provided.draggableProps}
>
// How to pass provided to children and then use it in DraggableColumn below
{props.children}
</div>
)}
</DraggableContainer>
const DraggableColumn = () => <DraggableContainer>
<div className={`${styles.column} ${scrollbarClassName}`}>
// How to get access to provided?
<div {...provided.dragHandleProps}>
<ColumnHeader {...columnHeaderProps} />
</div>
</div>
</DraggableContainer>
I think what you're looking for is something like the following:
const DraggableContainer = props => {
const provided = {
foo: "bar",
baz: () => console.log("hello world")
};
return props.children(provided);
};
const Draggable = props => {
return (
<DraggableContainer>
{provided => props.children(provided)}
</DraggableContainer>
);
};
const DraggableColumn = props => {
console.log('DraggableColumn.props', props)
return (
<div>
<h2>DraggableColumn</h2>
<p>provided: {JSON.stringify(props.provided)}</p>
</div>
);
};
const App = () => {
return (
<Draggable>{provided => <DraggableColumn provided={provided} />}</Draggable>
);
}
ReactDOM.render(
<App />,
document.getElementById("app")
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Basically, what you're doing here is recreating the same functionality that DraggableContainer provides by utilizing the children prop pattern:
reactjs.org/docs/render-props.html#using-props-other-than-render
One caveat is that Draggable will always be expecting props.children to be a function, meaning that the following will work:
<Draggable>
{(provided) => {
return <DraggableColumn provided={provided} />;
}}
</Draggable>
But this will not because props.children is not a function, unlike the example above:
<Draggable>
<p>Hello World</p>
</Draggable>
Hope this helps.
To pass a prop you need to declare it inside of the <>
For example
<Draggable provided={this.state.provided}>
<div>
Some Stuff
</div>
</Draggable>
Then you can do this.props.provided inside the draggable component.
I would like to show mapped posts from an API inside the OwlCarousel component (import OwlCarousel from 'react-owl-carousel') the code works just fine, but only outside the OwlCarousel component. You can test on this CodeSandbox https://codesandbox.io/s/friendly-rhodes-bv5ot, the code works when you remove the OwlCarousel tag.
renderPost = () => {
return this.state.posts
? this.state.posts.map(data => (
<div key={data.id} className="item">
<div className="heading">{data.subject}</div>
<div className="content">{data.message}</div>
</div>
))
: "Loading...";
};
render() {
return (
<div className="container">
<OwlCarousel className="owl-container owl-theme">
{this.renderPost()}
</OwlCarousel>
</div>
);
}
The code works only when i put the function outside the OwlCarousel component, i think it has something to do with scopes!
render() {
return (
<div className="container">
{this.renderPost()}
</div>
);
}
Try with following code
renderPost = () => {
return (
<div>
{this.state.posts
? this.state.posts.map(data => (
<div key={data.id} className="item">
<div className="heading">{data.subject}</div>
<div className="content">{data.message}</div>
</div>
))
: "Loading..."}
</div>
)
};
And In your render of the Component, you can do same as you have already done,
{this.renderPost()}
Considering I have a component which accepts one prop, what would be the easiest and most elegant way for both of these to work:
<MessageBlock message="Some message goes here" />
and
<MessageBlock message={() => (
<>
Some message <strong>goes</strong> here
</>
} />
What first comes to my mind is checking the prop type with typeof and rendering the prop according to that like this:
class MessageBlock extends React.Component {
render() {
const { message: Message } = this.props;
return (
<div className="message-block">
{typeof Message === "function" ? (
<Message />
) : (
<>
{message}
</>
)}
</div>
)
}
}
It depends on what your component should be able to do.
If you want your component to be able to display anything inside of it, I would recommend using its children prop as a render function :
const MessageBlock = props => (
<div className={'add the stuff you want to wrap'}>
{props.children()}
</div>
)
const App = props => (
<React.Fragment>
<MessageBlock>
{() =>
<React.Fragment>
Some message <strong>goes</strong> here
</React.Fragment>
}
</MessageBlock>
<MessageBlock>
{() => <p>Another message</p>}
</MessageBlock>
</React.Fragment>
)
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id='root'>
But if you want it to display a string and nothing else, use the propTypes with the first solution you proposed :
MessageBlock.propTypes = {
message: PropTypes.string
};
If you want it to do both, you could put a || condition with it, if the message is defined. The message will be shown by default if it exists, and otherwise the children function will be executed :
const MessageBlock = ({ children, message }) => (
<div className={'add the stuff you want to wrap'}>
{message || children()}
</div>
)
/*MessageBlock.propTypes = { //Proptypes are undefined in SO snippets :(
message: PropTypes.string
};*/
const App = props => (
<React.Fragment>
<MessageBlock>
{() =>
<React.Fragment>
Some message <strong>goes</strong> here
</React.Fragment>
}
</MessageBlock>
<MessageBlock message={'A string message'} />
</React.Fragment>
)
ReactDOM.render(<App/>, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id='root'>
I am using React. I have a list of images:
<div className="Images-gridWrapper">
{this.props.images.map((item, index) => {
return (
<div className="Images-gridItem" data-id={index} key={index}>
<img className="Images-gridImage" src={item.img.w1200}
onLoad={(e)=>{
// HERE
}}
/>
</div>
);
})}
</div>
I am looking for a way to add a class to each img tag when its image is loaded. I suppose that the onLoad function is the correct way, but how?
You can create a new component Img that has a state variable that you set to true when the image has been loaded. You can then use this state variable to add the additional class to the img element.
Example
class App extends React.Component {
render() {
return (
<div className="Images-gridWrapper">
{this.props.images.map((item, index) => {
return (
<div className="Images-gridItem" data-id={index} key={index}>
<Img src={item} />
</div>
);
})}
</div>
);
}
}
class Img extends React.Component {
state = { loaded: false };
onLoad = () => {
this.setState({ loaded: true });
console.log("Loaded!");
};
render() {
const { loaded } = this.state;
return (
<img
className={"Images-gridImage" + (loaded ? " Images-gridImage-loaded" : "")}
src={this.props.src}
onLoad={this.onLoad}
/>
);
}
}
ReactDOM.render(
<App
images={[
"http://placekitten.com/100/100",
"http://placekitten.com/100/200"
]}
/>,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>