Why is props sent from parent component become object in child component? - reactjs

I'am implementing a language translation system for my React app and I'm stuck on displaying the languages flags as Modal when the user click on the tranlation icon, which should display the list of languages to tranlate to. I have a LanguageModal.jsx child component which is a Modal triggered from NavigationBar.jsx parent component. These are the two components:
// Languagemodal.jsx
import React from 'react';
import './languageModal.css';
import en from '../../assets/flags/US.png';
import de from '../../assets/flags/DE.png';
import fr from '../../assets/flags/FR.png';
const languageModal = (show) => (show
? (
<div className="Modal">
<div className="Modal-content">
<div className="Modal-body">
<div><img src={en} alt="english" /></div>
<div><img src={de} alt="deutsch" /></div>
<div><img src={fr} alt="francais" /></div>
</div>
</div>
</div>
) : (null));
export default languageModal;
// NavigationBar.jsx
import React, { useState, useEffect } from 'react';
import { FaGlobe } from 'react-icons/fa';
import logo from '../../assets/logodidi.png';
import Modal from '../LanguageModal/languageModal.jsx';
import './navigationBar.css';
const NavigationBar = () => {
const [toggleBar, setToggleBar] = useState(false);
const [show, setShow] = useState(false);
useEffect(() => {
setShow(show);
}, [show]);
return (
<div className="navbar">
<div id="max-width">
<div className="navbar-items">
<div className="navbar-items_container">
<p>home</p>
<p>Usage</p>
<p>Categories</p>
<p>Blogs</p>
</div>
</div>
<div className="navbar-sign">
<p><FaGlobe color="#2b74b7" onClick={() => (setShow(true))} /></p>
<languageModal show={show} />
<button type="button">Get started</button>
</div>
</div>
</div>
);
};
export default NavigationBar;
The expected behavior is to display only when the user cliks on the FaGlob icon. The problem is that the Modal is automatically displayed without a click. so I set the variable "show" with React hook useState and useEffect to manage show/hide the Modal but not to avail, the Modal is always being displayed anyway. When I check the "show" value with docuemnt.write(show) I saw that it is "false" in the parent component but it gives [object object] in the languageModal child component. When I change the "show" initial value it doesn't take any effect inside the child languageModal component unless I manualy change it inside it by changing the
const languageModal = (show) => (show
? ( ... )
to
const languageModal = (show) => (!show
? (...)
And that is the weird point. Why the child component doesn't receive the value passed to it? Where is it taking the default value [object object] from, since "show" isn't an object but a boolean ? How to solve it then. Your help is very appreciated. I can't go forth whithout your help.

You are destructuring show incorrectly. Update it as:
const languageModal = ({show}) => (show
? (
<div className="Modal">
<div className="Modal-content">
<div className="Modal-body">
<div><img src={en} alt="english" /></div>
<div><img src={de} alt="deutsch" /></div>
<div><img src={fr} alt="francais" /></div>
</div>
</div>
</div>
) : (null));
export default languageModal;

When you write show like that, show will be props. That is why it is an object. If you want to extract a particular property, you can either do props.show or use destructuring in the functional component like this {show}
const languageModal = ({show}) => (show
? (
<div className="Modal">
<div className="Modal-content">
<div className="Modal-body">
<div><img src={en} alt="english" /></div>
<div><img src={de} alt="deutsch" /></div>
<div><img src={fr} alt="francais" /></div>
</div>
</div>
</div>
) : (null));

Related

Render component on button click to change page view with useState (or otherwise) in React

I've made a landing page for my portfolio and I'd like to have the page perform a transition animation (like a fadeout/In, but I can get to that later) then render the component of my actual portfolio instead of my landing page on the button click.
I started trying to write a function but am a bit lost with how to implement this.
I have this as my App setup so far but can't figure out how to implement the state change to render the Portfolio component instead of the HTML from App.js
import React, { useState } from "react";
import "./App.css";
// import Portfolio from "./Portfolio"
function App() {
//States
// const [view, setView] = useState();
// function viewPortfolio (
// )
return (
<div className="App">
<div className="title">
<h1 className="title maintext">Josh Bellingham</h1>
<h2 className="title subtext">Junior Web Developer</h2>
<button className="title btn">
<span>Welcome</span>
</button>
</div>
<div className="sun">
<img className="sun-img" src="Sun.png" alt="Sun" />
<div className="sun-ray r-one"></div>
<div className="sun-ray r-two"></div>
<div className="sun-ray r-three"></div>
<div className="sun-ray r-four"></div>
</div>
<div className="waves">
<img className="wave-1" src="Wave 1 (1).png" alt="Wave"></img>
<img className="wave-2" src="Wave 2 (1).png" alt="Wave"></img>
<img className="wave-3" src="Wave 3 (1).png" alt="Wave"></img>
</div>
</div>
);
}
just define a function in your code and call it with arrow functions or you can use the bind function. ps, it is good to use 'useMemo' or 'useCallback' hooks depending on your
function App() {
const [count, setCount] = useState(0);
//define function
const testFunction = () => {
console.log("you can call a function here ");
setCount(10); // or whatever you want to call
}
return (
<div className="App">
{/* call function */}
<button onClick={() => testFunction()}>test btn</button>
<p>{count}</p>
</div>
)
}
you can call any function with onClick method. if you just pass the function to the onClick, it will call your function when the component is being rendered and we don't want that for now, we wrap that in the arrow-function. every time you call a function and it changes any state in your component, it will cause re-rendering, so if you want to re-render you should change some states.
in the code I wrote when you click on the test btn you call testFunction and it will change the count state to 10 and it will cause re-rendering, we can use that state as I used it in the paragraph
you can read more at events or hooks ,lifecycle

Can someone tell me why my app crashes when I refresh the page?

I added a weather component in my app that fetches weather from open weather map. It works fine until I refresh the page, then it breaks.
If I comment the weather component out when refreshing and then add it back in when loaded it renders and works.
I'm not sure what's causing this error.
Here's some images of the console after refreshing for reference.
It seems to be undefined when refreshed. What's causing this issue?
// Weather component is called as normal in Home page
<div className="main-section-one">
<Weather />
<ToDoWidget />
</div>
import React, { useEffect, useState } from 'react'
//CSS
import '../css/Weather.css'
function Weather() {
// API
const URL = 'https://api.openweathermap.org/data/2.5/weather?q=barcelona&appid=APIKEY';
// State
const [weatherDetails, setWeatherDetails] = useState({});
async function getWeather() {
let fetchUrl = await fetch('https://api.openweathermap.org/data/2.5/weather?q=barcelona&appid=APIKEY&units=metric');
let data = await fetchUrl.json()
setWeatherDetails(data)
}
//Use Effect
useEffect(() => {
getWeather();
}, [])
return (
<div className="weather-container">
<div className="weather-one">
<div className="city">
<h3>Barcelona</h3>
<h1 className='temp'>{weatherDetails.main.temp}°C</h1>
</div>
<div className="current-weather">
<h3 className='current'>Sunny</h3>
</div>
</div>
<div className="weather-two">
<div className="">
<p>{weatherDetails.main.feels_like}°C</p>
<p className='weather-details'>Feels Like</p>
</div>
<div className="">
<p>{weatherDetails.main.humidity}%</p>
<p className='weather-details'>Humidity</p>
</div>
<div className="">
<p>{weatherDetails.wind.speed} MPH</p>
<p className='weather-details'>Wind Speed</p>
</div>
</div>
</div>
)
}
export default Weather
The main and wind properties may be undefined. Secure it.
<h1 className='temp'>{weatherDetails.main?.temp}°C</h1>
^^^ optional chaining
<p>{weatherDetails.wind?.speed} MPH</p>
Reference: Optional chaining

react component not working properly when rendered multiple times

Given below is code of a react component(Newsitem) and I have passed title as props through API. In Newsitem there is a span tag which I need to make hidden when the title passed in Newsitem contains less that 70 characters. But that doesn't happen, what happens is that whenever title has less than 70 characters so the span tag of first newsItem which was rendered get hidden and not of that newsitem to which that title belonged
export class NewsItem extends Component {
state = {
title: true
}
componentDidMount(){
let readTitle = document.getElementById('readTitle')
if(this.props.title.length<70){
readTitle.style.visibility = 'hidden'
console.log('Done....');
}
console.log('componentDidMount() lifecycle');
this.setState({title : !this.state.title})
}
render() {
console.log('Render lifecycle');
let {title , description , imageURL , newsURL} = this.props;
return (
<>
<div>
<div className="card" style={{width: '18rem'}}>
<img src={imageURL} className="card-img-top" alt="..."/>
<div className="card-body">
<h5 className="card-title" style={{display: 'inline'}} id='title'>{title}</h5>
<span id='readTitle'><b>Read More</b></span>
<p className="card-text">{description}</p>
Read More
</div>
</div>
</div>
</>
);
}
}
export default NewsItem;
Further to my comment, I would probably use a stateless functional component, and the React.useRef hook and do something like this:
import React, {useRef, useEffect} from 'react';
const NewsItem = ({title , description , imageURL , newsURL}) => {
const readTitle = useRef(null);
useEffect(()=>{
if(title.length<70){
readTitle.current.style.visibility = 'hidden'
console.log('Done....');
}
}, [title])
return (
<div>
<div className="card" style={{width: '18rem'}}>
<img src={imageURL} className="card-img-top" alt="..."/>
<div className="card-body">
<h5 className="card-title" style={{display: 'inline'}} id='title'>{title}</h5>
<span ref={readTitle}><b>Read More</b></span>
<p className="card-text">{description}</p>
Read More
</div>
</div>
</div>
);
};
export default NewsItem;
Should work as expected, and is considerably more concise :)
There is a very important rule in react and it is to not use js dom functions in the middle of components. If you use them you can create conflicts between components and states. You can instead use React refs in this case.

How to trigger a function from one component to another component in React.js?

I'am creating React.js Weather project. Currently working on toggle switch which converts celcius to fahrenheit. The celcius count is created in one component whereas toggle button is created in another component. When the toggle button is clicked it must trigger the count and display it. It works fine when both are created in one component, but, I want to trigger the function from another component. How could I do it? Below is the code for reference
CelToFahr.js (Here the count is displayed)
import React, { Component } from 'react'
import CountUp from 'react-countup';
class CeltoFahr extends Component {
state = {
celOn: true
}
render() {
return (
<React.Fragment>
{/* Code for celcius to farenheit */}
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<CountUp
start={!this.state.celOn ? this.props.temp.cel : this.props.temp.fahr}
end={this.state.celOn ? this.props.temp.cel : this.props.temp.fahr}
duration={2}
>
{({ countUpRef, start}) => (
<h1 ref={countUpRef}></h1>
)}
</CountUp>
</div>
</div>
</div>
</div>
{/*End of Code for celcius to farenheit */}
</React.Fragment>
)
}
}
export default CeltoFahr
CelToFahrBtn (Here the toggle button is created)
import React, { Component } from 'react'
import CelToFahr from './CeltoFahr'
class CelToFahrBtn extends Component {
state = {
celOn: true
}
switchCel = () => {
this.setState({ celOn: !this.state.celOn })
}
render = (props) => {
return (
<div className="button" style={{display: 'inline-block'}}>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<div onClick={this.switchCel} className="CelSwitchWrap">
<div className={"CelSwitch" + (this.state.celOn ? "" : " transition")}>
<h3>C°</h3>
<h3>F°</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CelToFahrBtn
Here when I click on switchCel it must trigger the celcius to fahrenheit value and vice-versa. How to do it? Any suggestions highly appreciated. Thanks in advance
I would have the celToFahr be the parent component of the celToFahrBtn and then pass the function you want to invoke via props
<CellToFahrBtn callback={yourfunction}/>
What else could you do is having a common parent for these to components where you would again do the execution via props and callbacks
The 3rd option would be having a global state which would carry the function like Redux or Reacts own Context. There again you would get the desired function via props and you would execute it whenever you like. This is the best option if your components are completely separated in both the UI and in source hierarchically, but I don't think this is the case in this case.
https://reactjs.org/docs/context.html
These are pretty much all the options you have
To achieve this you'd need to lift your state up and then pass the state and handlers to the needed components as props.
CeltoFahr & CelToFahrBtn would then become stateless components and would rely on the props that are passed down from TemperatureController
class TemperatureController extends Component {
state = {
celOn: true
}
switchCel = () => {
this.setState({ celOn: !this.state.celOn })
}
render () {
return (
<React.Fragment>
<CeltoFahr celOn={this.state.celOn} switchCel={this.state.switchCel} />
<CelToFahrBtn celOn={this.state.celOn} switchCel={this.state.switchCel}/>
</React.Fragment>
)
}
}
It's probably better explained on the React Docs https://reactjs.org/docs/lifting-state-up.html
See this more simplified example:
import React, {useState} from 'react';
const Display = ({}) => {
const [count, setCount] = useState(0);
return <div>
<span>{count}</span>
<Button countUp={() => setCount(count +1)}></Button>
</div>
}
const Button = ({countUp}) => {
return <button>Count up</button>
}
It's always possible, to just pass down functions from parent components. See Extracting Components for more information.
It's also pretty well described in the "Thinking in React" guidline. Specifically Part 4 and Part 5.
In React you should always try to keep components as dumb as possible. I always start with a functional component instead of a class component (read here why you should).
So therefore I'd turn the button into a function:
import React from 'react';
import CelToFahr from './CeltoFahr';
function CelToFahrBtn(props) {
return (
<div className="button" style={{ display: 'inline-block' }}>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<div onClick={() => props.switchCel()} className="CelSwitchWrap">
<div
className={'CelSwitch' + (props.celOn ? '' : ' transition')}
>
<h3>C°</h3>
<h3>F°</h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default CelToFahrBtn;
And you should put the logic in the parent component:
import React, { Component } from 'react';
import CountUp from 'react-countup';
import CelToFahrBtn from './CelToFahrBtn';
class CeltoFahr extends Component {
state = {
celOn: true
};
switchCel = () => {
this.setState({ celOn: !this.state.celOn });
};
render() {
return (
<>
<div className="weather">
<div className="figures">
<div className="figuresWrap2">
<div className="mainFigureWrap">
<CelToFahrBtn switchCel={this.switchCel} celOn={celOn} />
</div>
</div>
</div>
</div>
</>
);
}
}

How to use this react component efficiently?

In my react application, I create this CreateText component, which is used to write a post or comment on a post. This is the component.
import React from 'react';
import PropTypes from 'prop-types';
import { Header, TextAreaFieldGroup, Button, Spinner } from '../UI';
const CreateText = props => {
const { createPostData, textCreating, onInputChange, onCreateText } = props;
const setButtonText = () => {
let content = <Header Tag='span' text={createPostData.buttonText} />;
if (textCreating)
content = <Spinner width={20} height={20} borderWidth={4} borderTopWidth={4} />;
return content;
}
return (
<div className="post-form mb-3">
<div className="card card-info">
<div className="card-header bg-info text-white">
{createPostData.header}
</div>
<div className="card-body">
<form onSubmit={onCreateText}>
<div className="form-group">
<TextAreaFieldGroup
placeholder={createPostData.placeholder}
name={createPostData.name}
value={createPostData.text}
error={createPostData.error}
onChange={onInputChange}
/>
</div>
<Button
type='submit'
className='btn btn-dark col-md-2'
disabled={textCreating}
>
{setButtonText()}
</Button>
</form>
</div>
</div>
</div>
);
}
CreateText.propTypes = {
createPostData: PropTypes.object.isRequired,
onInputChange: PropTypes.func.isRequired,
onCreateText: PropTypes.func.isRequired
}
export default CreateText;
I didn't use a local state for this component. I used onInputChange() prop and implemented on the 2 parent components which I had used this component. My question is, what is the best way to implement this component? Having a state inside the component and use onChange() inside the component? Or pass it as a prop and implement in the parent component? Everywhere I used the approach that I have shown here. Which is the better approach?
If your verification for TextAreaFieldGroup is complex, your solution is good in this case. Because TextAreaFieldGroup receive createPostData from it parents.
If your verification is simple, just keep value on component's state and verify within component.
I just wondering why your form just has only one text field? If your form has more than one field, the best practice is using render-props pattern

Resources