React Props not passing down to children components? - javascript

I am trying to learn React so please bear with me!
I am following a tutorial to help me understand react and how you can pass down components.
I am trying to pass props down 2 levels, but when I render the code on the third element, nothing appears on the page. Using React Dev tools on chrome, it seems that the props are loading on the Tweets.js component rather than the Tweet.js component.
Can anybody tell me whats wrong? The order is App.js > Tweets.js > Tweet.js
For ref, I am following the following tutorial, it is around the 15 min mark.
React State and Props | Learn React For Beginners Part 4
App.js
import './App.css';
import Tweets from './components/Tweets';
import React from 'react';
function App() {
const name=["Name1", "Name2", "Name3"];
const age=["21", "22", "24"]; /* Data is created here */
return (
<div className="App">
<Tweets me={name} age={age} />{/*Data is added to component*/ }
</div>
);
}
export default App;
Tweets.js
import Tweet from './Tweet';
const Tweets = (props) => (
<section>
<Tweet />
</section>
);
export default Tweets;
Tweet.js
const Tweet = (props) => (
<div>
<h1>{props.me}</h1>
<h1>{props.age}</h1>
</div>
);
export default Tweet;

You would need to transfer props through your Tweets component:
const Tweets = (props) => (
<section>
<Tweet {...props} />
</section>
);

Related

Push to new route without any further actions on the component

We use an external componet which we don't control that takes in children which can be other components or
used for routing to another page. That component is called Modulation.
This is how we are currently calling that external Modulation component within our MyComponent.
import React, {Fragment} from 'react';
import { withRouter } from "react-router";
import { Modulation, Type } from "external-package";
const MyComponent = ({
router,
Modulation,
Type,
}) => {
// Need to call it this way, it's how we do modulation logics.
// So if there is match on typeA, nothing is done here.
// if there is match on typeB perform the re routing via router push
// match happens externally when we use this Modulation component.
const getModulation = () => {
return (
<Modulation>
<Type type="typeA"/> {/* do nothing */}
<Type type="typeB"> {/* redirect */}
{router.push('some.url.com')}
</Type>
</Modulation>
);
}
React.useEffect(() => {
getModulation();
}, [])
return <Fragment />;
};
export default withRouter(MyComponent);
This MyComponent is then called within MainComponent.
import React, { Fragment } from 'react';
import MyComponent from '../MyComponent';
import OtherComponent1 from '../OtherComponent1';
import OtherComponent2 from '../OtherComponent2';
const MainComponent = ({
// some props
}) => {
return (
<div>
<MyComponent /> {/* this is the above component */}
{/* We should only show/reach these components if router.push() didn't happen above */}
<OtherComponent1 />
<OtherComponent2 />
</div>
);
};
export default MainComponent;
So when we match typeB, we do perform the rerouting correctly.
But is not clean. OtherComponent1 and OtherComponent2 temporarily shows up (about 2 seconds) before it reroutes to new page.
Why? Is there a way to block it, ensure that if we are performing router.push('') we do not show these other components
and just redirect cleanly?
P.S: react-router version is 3.0.0

How to render different style css on same component?

I have react component which is a button and I render this component three times. I want to add some CSS on the second component but I don't know how. I tried to add some class names, but then I can't figure it out where to put this style in the CSS.
I can change css in element.style in dev tools but can't in project.
import './App.css';
import './flow.css';
import './neonButton.css';
import GlowBox from './GlowBox';
import NavBar from './NavBar';
function App() {
return (
<div>
<div className='divBut'>
<NavBar></NavBar>, <NavBar className='drugi'></NavBar>,<NavBar></NavBar>
</div>
<GlowBox></GlowBox>
</div>
);
}
export default App;
import styled from 'styled-components';
const NavBar = (props) => {
return (
<div>
<Container>
<a class='neon'>Neon</a>
</Container>
</div>
);
};
const Container = styled.div`
background-color: transparent;
`;
export default NavBar;
I try to add props to component
<!-- begin snippet: js hide: false console: true babel: false -->
and then add a type to a component like this
const NavBar = (type) => {
return (
<div>
<Container>
<a class={`neon ${type}`}>Neon</a>
</Container>
</div>
);
};
<NavBar></NavBar>, <NavBar type='drugi'></NavBar>,<NavBar></NavBar>
but nothing is change.
You have props that you don't use, this is a good simple read on How to Pass Props to component, you can adjust this to other needs, this is example...:
import styled from 'styled-components';
const NavBar = ({class}) => {
return (
<div>
<Container>
<a class={class}>Neon</a>
</Container>
</div>
);
};
const Container = styled.div`
background-color: transparent;
`;
export default NavBar;
...
import './App.css';
import './flow.css';
import './neonButton.css';
import GlowBox from './GlowBox';
import NavBar from './NavBar';
function App() {
const NavStyles = {
className1: 'neon',
className2: 'drugi'
};
return (
<div>
<div className='divBut'>
<NavBar class={NavStyles.className1}></NavBar>, <NavBar class={NavStyles.className2}></NavBar>,<NavBar class={NavStyles.className1}></NavBar>
</div>
<GlowBox></GlowBox>
</div>
);
}
export default App;
Edit: Given that you have edited your question I have new information for you.
1.) You can't use the reserved word class in React, because class means something different in Javascript than it does in html. You need to replace all instances of class with className.
2.) Did you notice how in the devtools on your button it says the className says: neon [object object]?
You should use a ternary operator to handle the cases where you don't pass the type prop.
ex.) class={neon ${props?.type !== undefined ? type ''}}
3.) You are trying to apply a className to a component, which does not work. The className attribute can only be applied directly to JSX tags like h1, div, etc. Use a different prop name, then you can use that to decide the elements className.

Why don't my images and information render on my React localhost?

Recently I started a React course where the chapter goal is to create a monster sort of website. Below I will leave the code of the relevant JS and JSX files. My SearchBox input does appear on my screen, however, the monster images with their respective h2 and p are not appearing only in my react localhost. I have tried going through my code to understand why my code is not working and I haven't been able to find a solution. Here I will leave the link to the API where I am obtaining the images from, you just have to change the number before the ? to access the other images. I am aware that classes are a bit outdated due to hooks but the course is focusing on them initially so that we can understand their behavior, so please do not update the code, just help me with the functionality.
https://robohash.org/1?set=set2&size=180x180
App.js File
import React, {Component} from 'react';
import './App.css';
import { CardList } from './components/card-list/card-list';
import {SearchBox} from './components/search-box/search-box';
class App extends Component {
constructor() {
super();
this.state = {
monsters: [],
searchField: ''
};
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(users => this.setState({monsters: users}));
}
render() {
const {monsters, searchField} = this.state;
const filteredMonsters = monsters.filter(monster => monster.name.toLowerCase().includes(searchField.toLowerCase())) //This checks if one of the monsters name includes the text inputted into the search field, and filters out those that do not. We lower case it to avoid capital and lower case issues.
return (
<div className='App'>
<SearchBox
placeholder='Search Monsters'
handleChange={e => this.setState({searchField: e.target.value})}
/>
<CardList monsters={filteredMonsters}/>
</div> //By using filteredMonsters here, initially all will appear but as we place some input, only those that include the searchField will appear
)
}
};
export default App;
card.jsx file
import React from 'react';
export const Card = props => {
return (
<div>
<img src={'https://robohash.org/'+ props.monster.id + '?set=set2&size=180x180'} alt='monster'/>
<h2> {props.monster.name} </h2>
<p> {props.monster.email} </p>
</div>
)
}
card-list.jsx file
import React from 'react';
import { Card} from '../card/card';
import './card-list.css';
export const CardList = props => {
return (
<div className='card-list'>
{props.monsters.map((monster) => {
<Card key={monster.id}/>
})}
</div>
)
};
In CardList, you never even passed a monster object to your Card component. Only a monster id for a key. Basically, you need something like this:
<div className='card-list'>
{props.monsters.map((monster) => {
<Card key={monster.id} monster={monster} />
})}
</div>
Minor suggestion: Use proper destructuring in Card component:
export const Card = ({ monster: { id, name, email } }) => {
return (
<div>
<img src={'https://robohash.org/'+ id + '?set=set2&size=180x180'} alt='monster'/>
<h2> {name} </h2>
<p> {email} </p>
</div>
)
}
Working Sandbox: https://codesandbox.io/s/fervent-cerf-qcubt?file=/src/App.js

Learning React: how to useRef in custom component?

I'm learning React and I don't think I understand the concept of useRef properly. Basically, I want to include some tags in tagify input field when a user clicks on a chip that is rendered outside the input box.
My idea is to do something like this (App.js):
import Chip from '#material-ui/core/Chip';
import Tagify from "./Tagify"
...
class App extends React.Component {
...
const { error, isLoaded, quote, tags } = this.state; //tags comes from the server
var tagify = <Tagify tags={tags} />
const addTagOnChipClick = (tag) => {
tagify.addTag(tag)
};
const chips = tags.map(tag => (
<span key={tag.name} className="chips">
<Chip
label={tag.name}
variant="outlined"
onClick={addTagOnChipClick(tag)}
clickable
/>
</span>
))
...
}
The tagify documentation says that
To gain full access to Tagify's (instance) inner methods, A custom ref can be used: <Tags tagifyRef={tagifyRef} ... />
My attempt to gain access to these inner methods was to use useRef (Tagify.js):
import Tags from '#yaireo/tagify/dist/react.tagify'
import '#yaireo/tagify/dist/tagify.css'
export default function Tagify(tags) {
const tagifyRef = useRef()
return (
<Tags
tagifyRef={tagifyRef}
placeholder='Filter by tags...'
whitelist={tags.tags}
/>
)
}
However, tagifyRef.current is undefined. What I'm doing wrong? There's another way to access the inner methods?
Thank you very much!
When are you accessing the ref? Make sure you access the ref only after the component has mounted i.e. in a useEffect:
import Tags from '#yaireo/tagify/dist/react.tagify'
import '#yaireo/tagify/dist/tagify.css'
export default function Tagify(tags) {
const tagifyRef = useRef()
React.useEffect(() => {
console.log(tagifyRef.current)
}, [])
return (
<Tags
tagifyRef={tagifyRef}
placeholder='Filter by tags...'
whitelist={tags.tags}
/>
)
}

What is the best way to go about conditional rendering in the case of very similar components?

I render different landing pages based on whether the user is a professor, student, or not logged in. The landing pages are very similar; the only difference is the buttons displayed. I know I can go around this using inline conditions or simple if-else statements. However, I was wondering what the best practices are to implement conditional rendering in this case. I know higher order components (HOCs) can help but I was not sure if they are overkill in this particular case.
To be on the same page, here are the different Landing components that I currently render using if-else statements.
Landing.js (unlogged users):
import React from 'react';
import { withRouter } from 'react-router-dom';
import { compose } from 'recompose';
import { withEither } from '../../helpers/withEither';
import LandingStudent from './LandingStudent';
import LandingProfessor from './LandingProfessor';
import './Landing.css';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
</div>
</div>
);
};
export default Landing;
LandingProfessor.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';
const LandingProfessor = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
<RaisedButton
className="btn-animated btn-landing"
label="Create Class"
onClick={() => history.push('/courses/new')}
/>
<RaisedButton
className="btn-animated btn-landing"
label="Dashboard"
onClick={() => history.push('/courses')}
/>
</div>
</div>
);
};
export default withRouter(LandingProfessor);
LandingStudent.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import RaisedButton from 'material-ui/RaisedButton';
import './Landing.css';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<h1 className="header-primary">
<span className="header-primary-main">
QME
</span>
<span className="header-primary-sub">
the best way to ask questions
</span>
</h1>
<RaisedButton
className="btn-animated btn"
label="Join Class"
onClick={() => history.push('/courses/join')}
/>
</div>
</div>
);
};
export default withRouter(Landing);
A 'trick' could be to append a className on the root div for landscape, namely 'student' 'professor' or 'logout' and use css display: none to hide unwanted items in each scenarios
Another approach would be to keep your 3 components but delegate all the rendering to a common 'LandingRenderer' component that would accept boolean properties like 'showJoinClassButton' 'showCreateButtonButton' etc.
Then your 3 components render would look like domething like this
LandingProfessor: (props) => (
<LandingRenderer
showJoinClassButton={false}
showCreateClassButton={true}
...
{...props} />
)
I would opt for composition over inheritance here, meaning create many reusable components and compose your top-level components with those reusable ones rather than make your top-level components inherit from one common component type. If you follow the latter approach, you might end up with a laundry list of props that I argue is questionably better than what you have right now.
To start, you could componentize your header:
Header.js
export default function Header(props) {
return (
<h1 className="header-primary">
{props.children}
</h1>
);
}
Header.Main = function HeaderMain(props) {
return (
<span className="header-primary-main">
{props.children}
</span>
);
};
Header.Sub = function HeaderSub(props) {
return (
<span className="header-primary-sub">
{props.children}
</span>
);
};
and use it in Landing.js:
import Header from './Header.js';
const Landing = ({ history }) => {
return(
<div className="header">
<div className="text-box">
<Header>
<Header.Main>QME</Header.Main>
<Header.Sub>the best way to ask questions</Header.Sub>
</Header>
</div>
</div>
);
}
I don't think hoc in this particular case is an overkill.
I think whenever you can use hoc for better readability and useability, use it.
Using some of recompose hocs (you should install recompose: npm install recompose)
export const renderByConditions = (...conditions) => compose(
...conditions.map(condition =>
branch(condition[0], (condition[1] && renderComponent(condition[1])) || renderNothing, condition[2] && renderComponent(condition[2]))
)
)
You should pass arrays with the signature:
[condition, left (if the condition is true), right (else)]
condition - (ownProps) => youCondition
left - Component | empty
right - Component | empty
when left is empty - if the condition is true, it will render null.
when right is empty - if the condition is not true, it will render the component we wrapped
Then you can use the hoc:
renderByConditions(
[props => props.landing, props => <Landing {...props}/>],
[props => props.proffesor, LandingProfessor],
[props => props.student, LandingStudent],
[Component => Component, DefaultComponent]
)
I would reccommend start using recompose hocs, they are great!

Categories

Resources