React children issue (typescript) - javascript

I will provide all the code needed for my issue, I am stumped as to how to solve this I have tried many things none of which worked.
export class JobBuilderOptimise extends React.Component<JobBuilderOptimiseProps & JobBuilderOptimiseDispatch> {
render() {
const {
jobPosting,
search,
previewByTemplate
} = this.props.pageState;
return (
<div className="optimise-page">
<Row>
<Col sm={7} >
<div className="optimise-panel">
{this.props.children}
</div>
</Col>
<Col sm={5}>
<div className="preview-panel">
<JobPostingControl jobDescription={jobPosting.data} isLoading={search.previewRequestState.isBusy || previewByTemplate.isBusy} leftSide={this.props.children}/>
</div>
</Col>
</Row>
</div>
);
}
this.props.children either displays JobBuilderModel.tsx or JobBuilderSearch.tsx. I can provide the code for each if neccessary but the main identifying factors I can find are the props:
interface JobBuilderModelProps {
pageState: BuilderState;
}
interface JobBuilderSearchProps {
pageState: SearchState;
}
and also the classNames of the container divs:
<div className="search-job-section">
...
</div>
<div className="model-job-section">
...
</div>
The reason I have provided these are because I have to figure out which of the two react components will be displayed (and I have been trying to distinguish the two in any way in order to identify which actually is being displayed but maybe that is not the right way to go about it), and based on that, have to pass a string into the JobPostingControl control that is also on the page. Let me know if any more details are needed.

If I understand you correctly the problem is how to find out what type of component is inside children of the JobBuilderOptimise component. If so - you can use type property of the child - that will return you type of the respective child.
Then you can test to see what inside to the code similar to:
if((child.type as any).prototype instanceof JobBuilderModel)
{
}

Related

TailwindCSS overflow-auto class not working with React Lists

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

Why does my React grandchild component not update after receiving a change in props from its parent? Or: do I need to use state?

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.

Rendering Component in Carousel using React

I want to achieve a carousel like Materialize.
Have an API from where I am fetching the data, so according to Materialize
I compared the console or Materialize default and my rendered components.
I guess the problem is, it's not inheriting the properties of carousel-item
Class carousel-item is supposed to Render inside of Class carousel.
<div className="carousel">
// These are supposed to be dynamic, below component is not present here
<div className="carousel-item">
</div>
</div>
How I am trying to render the data is in this manner.
renderAlbums(){
return this.state.albums.map(album =>
<Card song={album.name} singer={album.artist_name} src={album.cover_photo_url}/>
);
}
Rendered the data <Card />(It contains the class of carousel-item), which is supposed to place Card containing class of carousel-item.
class Carousel extends Component {
state = { albums: [] };
componentWillMount() {
axios.get('https://cors-anywhere.herokuapp.com/https://stg-resque.hakuapp.com/albums.json')
.then(response => this.setState({albums: response.data}));
}
renderAlbums(){
return this.state.albums.map(album =>
<div className="carousel-item"><Card key={album.name} song={album.name} singer={album.artist_name} src={album.cover_photo_url}/></div>
);
}
render() {
return (
<div className="carousel center">
{this.renderAlbums()}
</div>
);
}
}
export default Carousel;
This is my Card component
class Card extends Component {
render() {
return (
<div className="card z-depth-4">
<div>
<img src={this.props.src} />
</div>
<p>{this.props.song}</p>
<div className="singer">{this.props.singer}</div>
</div>
);
}
}
export default Card;
EDIT:
Want that content to display like this.
But it's not working the way it's expected.
Please suggest me, what am I doing wrong?
In axios.get, I see that you are using proxy link.
One reason is, it can be creating problems.
Other reason can be you are trying to put carousel-item into carousel.
Try adding center class to both i.e. carousel as well as carousel-item.
Check if these works.
First of all, there is nothing in your Carousel that says which element is active. You need to have a state variable that points to the active element.
Then you only need to draw [-2, -1, 0, 1, 2] offsets vs the active one. And each rendered card needs to know which offset to know their style.

React JS | Render Multiple Elements

I am trying to create a email in React using the MJML email library. It runs off react and I have it all working but I need to render 2 sections rather than 1. When I render 1 it doesn't appear properly on the webpage as I need them to be different sizes.
When I try and wrap the elements within a array the return become null, take out one of the sections and it gets returned.
Any help would be appreciated, here is the code.
render() {
const { mjAttribute } = this.props
const content = [this.renderEmailOverhead()]
const innerContent = [this.renderEmailBanner(), this.renderEmailTitle(), this.renderEmailText(), this.renderEmailDivider]
return ([
<Section full-width='full-width' padding-top="0">
{ content }
</Section>,
<Section>
{ innerContent }
</Section>
])
}
Well, render method of a component can only return one element. so you'll have to wrap it in a divas Zargold mentioned.
Note that MJML component are more than a standard React component.
It has some internal logic not available in a React context. IMO you should generate MJML as standard HTML element and render it with a renderToStaticMarkup then pass it to mjml2html function as a string and mjml will compiles
return (
<mjml>
<mj-body>
<mj-container>
... // your sections goes here
</mj-container>
</mj-body>
</mjml>
)
Note that I don't think React is the best suited for this kind of work, I would recommend you to use a templating language such as mustache/handlebars which fit better.
You cannot use JSX interspersed with JavaScript like that... you could either do (you must have only one parent/root element).
<div>
<Section full-width='full-width' padding-top="0">
{ content }
</Section>
<Section>
{ innerContent }
</Section>
</div>
Or You could if you insist on using an array for some reason:
renderSection(content, fullWidth){
return (
<Section
full-width={fullWidth ? 'full-width' : false}
style={{paddingTop: fullWidth ? 0 : 'auto'}}
>
{content}
</Section>
)
}
render(){
let contents = [content, innerContent]
return(
<div>
{contents.map(section, i => renderSection(section, i % 2 === 0))
</div>
)

React - passing ref to sibling

I need to have 2 sibling components, and 1 of them has to have a reference to another - is that possible?
I tried this:
<button ref="btn">ok</button>
<PopupComponent triggerButton={this.refs.btn} />
And even this
<button ref="btn">ok</button>
<PopupComponent getTriggerButton={() => this.refs.btn} />
But I get undefined in both situations. Any ideas?
Note: a parent component will not work for me, because I need to have multiple sets of those, like
<button />
<PopupComponent />
<button />
<PopupComponent />
<button />
<PopupComponent />
NOT like this:
<div>
<button />
<PopupComponent />
</div>
<div>
<button />
<PopupComponent />
</div>
<div>
<button />
<PopupComponent />
</div>
Think this question is best answered by the docs:
If you have not programmed several apps with React, your first
inclination is usually going to be to try to use refs to "make things
happen" in your app. If this is the case, take a moment and think more
critically about where state should be owned in the component
hierarchy. Often, it becomes clear that the proper place to "own" that
state is at a higher level in the hierarchy. Placing the state there
often eliminates any desire to use refs to "make things happen" –
instead, the data flow will usually accomplish your goal.
Not sure exactly what you are trying to do, but my hunch is a parent component and passing props is what you really want here.
I completely agree with the quote Mark McKelvy has provided. What you are trying to achieve is considered an anti-pattern in React.
I'll add that creating a parent component doesn't necessarily means it has to be a direct parent, you can create a parent component further up the chain, in which you can render an array of all your children components together, having the logic to coordinate between all the children (or pairs of children according to your example) sit inside your parent.
I created a rough example of the concept which should do the trick:
class A extends React.Component {
onClick(key) {
alert(this.refs[key].refs.main.innerText);
}
render() {
var children = [];
for (var i = 0; i < 5; i++)
children.push.apply(children, this.renderPair(i));
return (
<div>
{children}
</div>
);
}
renderPair(key) {
return [
<B ref={'B' + key} key={'B' + key} onClick={this.onClick.bind(this, 'C' + key)}/>,
<C ref={'C' + key} key={'C' + key} onClick={this.onClick.bind(this, 'B' + key)}/>
];
}
}
class B extends React.Component {
render() {
return <p ref="main" onClick={this.props.onClick}>B</p>;
}
}
class C extends React.Component {
render() {
return <p ref="main" onClick={this.props.onClick}>C</p>;
}
}
React.render(<A/>, document.getElementById('container'));
And any state you need to save for all your children, you do in the common parent. I really hope this helps.
The following code helps me to setup communication between two siblings. The setup is done in their parent during render() and componentDidMount() calls. Hope it helps.
class App extends React.Component<IAppProps, IAppState> {
private _navigationPanel: NavigationPanel;
private _mapPanel: MapPanel;
constructor() {
super();
this.state = {};
}
// `componentDidMount()` is called by ReactJS after `render()`
componentDidMount() {
// Pass _mapPanel to _navigationPanel
// It will allow _navigationPanel to call _mapPanel directly
this._navigationPanel.setMapPanel(this._mapPanel);
}
render() {
return (
<div id="appDiv" style={divStyle}>
// `ref=` helps to get reference to a child during rendering
<NavigationPanel ref={(child) => { this._navigationPanel = child; }} />
<MapPanel ref={(child) => { this._mapPanel = child; }} />
</div>
);
}
}
special-props
Special Props Warning
Most props on a JSX element are passed on to the component, however, there are two special props (ref and key) which are used by React, and are thus not forwarded to the component.
For instance, attempting to access this.props.key from a component (eg. the render function) is not defined. If you need to access the same value within the child component, you should pass it as a different prop (ex: ). While this may seem redundant, it's important to separate app logic from reconciling hints.

Categories