Rendering specific component using switch case --- React - reactjs

My requirement is to render components based on user selection.
I have a left nav on click of which I am trying to render the component associated with it but I am getting error:
Error:
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
My code goes as under:
------ManageData.js-------
import React, {
Component
} from 'react';
import NestedList from '../Left-nav/leftnav';
import '../Left-nav/leftnav.css';
import AddCarousel from '../addCarousel/addCarousel';
import AddClass from '../addClass/addClass';
import './manageData.css';
class ManageData extends Component {
loadComponent = null;
constructor(props) {
super(props);
this.state = {
showChild: 1
};
//this.generateComponent = this.generateComponent.bind(this);
}
loadComponents = (data) => {
console.log(data, this.state);
if (data == 'AddClass') {
this.setState({
showChild: 2
}, () => {
console.log('state changed-----', this.state)
//this.generateComponent(data, this.state);
})
}
}
render() {
const showItem = this.state.showChild;
return (
<section className = "Admin-add-video">
<div className="row">
<div className = "col-3 set-padding" > < NestedList loadComponents = {this.loadComponents}/>
</div >
<div className = "col-9 set-ht" >
{ this.state.showChild == 1 && <AddCarousel/> }
{this.state.showChild == 2 && <AddClass/>}
</div>
</div>
</section>
);
}
}
export default ManageData;
Nested List is the separate component on click of its item I am getting the value and trying to setState().
I have tried everything from this url : using switch statement for react rendering
But for all the cases I am getting same error.
May be I am missing anything. Any help will be highly appreciated.

It looks like the problem is with AddClass component. Pl double check if it is exported correctly.
Note: Posting this answer from my comment on the question as it fixed OP's error.

Try this in the render method:
render() {
const { showChild } = this.state;
const renderChilds = () => {
switch(showChild) {
case 1:
return <AddCarousel />;
case 2:
return <AddClass />;
default:
return <div />;
}
};
return (
<section className="Admin-add-video">
<div className="row">
<div className = "col-3 set-padding">
< NestedList loadComponents={this.loadComponents} />
</div >
<div className = "col-9 set-ht" >
{renderChilds()}
</div>
</div>
</section>
);
}

Related

Change **child element** className with parent onClick [duplicate]

I am trying to figure out how to toggle an active class on click to change CSS properties.
My code is below. Can anyone advise how I should do this? Without creating a new component for each item is it possible to do this?
class Test extends Component(){
constructor(props) {
super(props);
this.addActiveClass= this.addActiveClass.bind(this);
}
addActiveClass() {
//not sure what to do here
}
render() {
<div>
<div onClick={this.addActiveClass}>
<p>1</p>
</div>
<div onClick={this.addActiveClass}>
<p>2</p>
</div>
<div onClick={this.addActiveClass}>
<p>3</p>
</div>
</div>
}
}
Use state. See the React docs.
class MyComponent extends Component {
constructor(props) {
super(props);
this.addActiveClass= this.addActiveClass.bind(this);
this.state = {
active: false,
};
}
toggleClass() {
const currentState = this.state.active;
this.setState({ active: !currentState });
};
render() {
return (
<div
className={this.state.active ? 'your_className': null}
onClick={this.toggleClass}
>
<p>{this.props.text}</p>
</div>
)
}
}
class Test extends Component {
render() {
return (
<div>
<MyComponent text={'1'} />
<MyComponent text={'2'} />
</div>
);
}
}
You can also do this with hooks.
function MyComponent (props) {
const [isActive, setActive] = useState(false);
const toggleClass = () => {
setActive(!isActive);
};
return (
<div
className={isActive ? 'your_className': null}
onClick={toggleClass}
>
<p>{props.text}</p>
</div>
);
}
I would prefer using the && operator in an inline if statement. In my opinion it gives cleaner codebase this way.
Generally you could be doing something like this:
render(){
return(
<div>
<button className={this.state.active && 'active'}
onClick={ () => this.setState({active: !this.state.active}) }>Click me</button>
</div>
)
}
Just keep in mind that arrow functions are and ES6 feature and remember to set this.state.active value in the class constructor.
this.state = { active: false }
Or if you want to inject CSS in JSX you are able to do it this way:
<button style={this.state.active && style.button} >button</button>
And you can declare style json variable:
const style = { button: { background:'red' } }
Remember to use camelCase on JSX stylesheets.
Well, your addActiveClass needs to know what was clicked. Something like this could work (notice that I've added the information which divs are active as a state array, and that onClick now passes the information what was clicked as a parameter after which the state is accordingly updated - there are certainly smarter ways to do it, but you get the idea).
class Test extends Component(){
constructor(props) {
super(props);
this.state = {activeClasses: [false, false, false]};
this.addActiveClass= this.addActiveClass.bind(this);
}
addActiveClass(index) {
const activeClasses = [...this.state.activeClasses.slice(0, index), !this.state.activeClasses[index], this.state.activeClasses.slice(index + 1)].flat();
this.setState({activeClasses});
}
render() {
const activeClasses = this.state.activeClasses.slice();
return (
<div>
<div className={activeClasses[0]? "active" : "inactive"} onClick={() => this.addActiveClass(0)}>
<p>0</p>
</div>
<div className={activeClasses[1]? "active" : "inactive"} onClick={() => this.addActiveClass(1)}>
<p>1</p>
</div>
<div onClick={() => this.addActiveClass(2)}>
<p>2</p>
</div>
</div>
);
}
}
You can simply access the element classList which received the click event using event.target then by using toggle method on the classList object to add or remove the intended class
<div onClick={({target}) => target.classList.toggle('active')}>
....
....
....
</div>
Equevelent
<div onClick={e=> e.target.classList.toggle('active')}>
....
....
....
</div>
OR by declaring a function that handle the click and does extra work
function handleClick(el){
.... Do more stuff
el.classList.toggle('active');
}
<div onClick={({target})=> handleClick(target)}>
....
....
....
</div>
React has a concept of components state, so if you want to switch it, do a setState:
constructor(props) {
super(props);
this.addActiveClass= this.addActiveClass.bind(this);
this.state = {
isActive: false
}
}
addActiveClass() {
this.setState({
isActive: true
})
}
In your component use this.state.isActive to render what you need.
This gets more complicated when you want to set state in component#1 and use it in component#2. Just dig more into react unidirectional data flow and possibly redux that will help you handle it.
using React you can add toggle class to any id/element, try
style.css
.hide-text{
display: none !important;
/* transition: 2s all ease-in 0.9s; */
}
.left-menu-main-link{
transition: all ease-in 0.4s;
}
.leftbar-open{
width: 240px;
min-width: 240px;
/* transition: all ease-in 0.4s; */
}
.leftbar-close{
width: 88px;
min-width:88px;
transition: all ease-in 0.4s;
}
fileName.js
......
ToggleMenu=()=>{
this.setState({
isActive: !this.state.isActive
})
console.log(this.state.isActive)
}
render() {
return (
<div className={this.state.isActive===true ? "left-panel leftbar-open" : "left-panel leftbar-close"} id="leftPanel">
<div className="top-logo-container" onClick={this.ToggleMenu}>
<span className={this.state.isActive===true ? "left-menu-main-link hide-from-menu" : "hide-text"}>Welcome!</span>
</div>
<div className="welcome-member">
<span className={this.state.isActive===true ? "left-menu-main-link hide-from-menu" : "hide-text"}>Welcome<br/>SDO Rizwan</span>
</div>
)
}
......
The above answers will work, but just in case you want a different approach, try classname: https://github.com/JedWatson/classnames
A good sample would help to understand things better:
HTML
<div id="root">
</div>
CSS
.box {
display: block;
width: 200px;
height: 200px;
background-color: gray;
color: white;
text-align: center;
vertical-align: middle;
cursor: pointer;
}
.box.green {
background-color: green;
}
React code
class App extends React.Component {
constructor(props) {
super(props);
this.state = {addClass: false}
}
toggle() {
this.setState({addClass: !this.state.addClass});
}
render() {
let boxClass = ["box"];
if(this.state.addClass) {
boxClass.push('green');
}
return(
<div className={boxClass.join(' ')} onClick={this.toggle.bind(this)}>{this.state.addClass ? "Remove a class" : "Add a class (click the box)"}<br />Read the tutorial here.</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
you can add toggle class or toggle state on click
class Test extends Component(){
state={
active:false,
}
toggleClass() {
console.log(this.state.active)
this.setState=({
active:true,
})
}
render() {
<div>
<div onClick={this.toggleClass.bind(this)}>
<p>1</p>
</div>
</div>
}
}
Thanks to #cssko for providing the correct answer, but if you tried it yourself you will realise it does not work. A suggestion has been made by #Matei Radu, but was rejected by #cssko, so the code remains unrunnable (it will throw error 'Cannot read property bind of undefined'). Below is the working correct answer:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.addActiveClass = this.addActiveClass.bind(this);
this.state = {
active: false,
};
}
addActiveClass() {
const currentState = this.state.active;
this.setState({
active: !currentState
});
};
render() {
return ( <
div className = {
this.state.active ? 'your_className' : null
}
onClick = {
this.addActiveClass
} >
<
p > {
this.props.text
} < /p> < /
div >
)
}
}
class Test extends React.Component {
render() {
return ( <
div >
<
MyComponent text = {
'Clicking this will toggle the opacity through css class'
}
/> < /
div >
);
}
}
ReactDOM.render( <
Test / > ,
document.body
);
.your_className {
opacity: 0.3
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js"></script>
React has a concept of components state, so if you want to Toggle, use setState:
App.js
import React from 'react';
import TestState from './components/TestState';
class App extends React.Component {
render() {
return (
<div className="App">
<h1>React State Example</h1>
<TestState/>
</div>
);
}
}
export default App;
components/TestState.js
import React from 'react';
class TestState extends React.Component
{
constructor()
{
super();
this.state = {
message: 'Please subscribe',
status: "Subscribe"
}
}
changeMessage()
{
if (this.state.status === 'Subscribe')
{
this.setState({message : 'Thank You For Scubscribing.', status: 'Unsubscribe'})
}
else
{
this.setState({ message: 'Please subscribe', status: 'Subscribe' })
}
}
render()
{
return (
<div>
<h1>{this.state.message}</h1>
<button onClick={()=> this.changeMessage() } >{this.state.status}</button>
</div>
)
}
}
export default TestState;
Output
I started learning React recently and wanted to build a tab just to see how far my knowledge has gone. I came across this and decided to implement something without redux. I kind of feel the answers don't reflect what op wants to achieve. He wants only one active component but the answers here will set all components active. I have given it a shot.
Below is a tab file
import React, { Component } from 'react';
class Tab extends Component {
render(){
const tabClassName = "col-xs-3 tab-bar";
const activeTab = this.props.activeKey === this.props.keyNumber ? "active-tab" : null;
return (
<div
className = {`${tabClassName} ${activeTab}`}
onClick={()=>this.props.onClick(this.props.keyNumber)}
>
I am here
</div>
);
}
}
export default Tab;
The tabs file...
import React, { Component } from 'react';
import Tab from './tab';
class Tabs extends Component {
constructor(props){
super(props);
this.state = {
currentActiveKey: 0,
tabNumber: 2
};
this.setActive = this.setActive.bind(this);
this.setTabNumber = this.setTabNumber.bind(this);
}
setTabNumber(number){
this.setState({
tabNumber: number
});
}
setActive (key){
this.setState({
currentActiveKey: key
});
}
render(){
let tabs = [];
for(let i = 0; i <= this.state.tabNumber; i++){
let tab = <Tab key={i} keyNumber={i} onClick={this.setActive} activeKey={this.state.currentActiveKey}/>;
tabs.push(tab);
}
return (
<div className="row">
{tabs}
</div>
);
}
}
export default Tabs;
your index file...
import React from 'react';
import ReactDOM from 'react-dom';
import Tabs from './components/tabs';
ReactDOM.render(
<Tabs />
, document.querySelector('.container'));
and the css
.tab-bar {
margin: 10px 10px;
border: 1px solid grey;
}
.active-tab {
border-top: 1px solid red;
}
This is a skeleton of something I want to improve on so increasing the tabNumber beyond 4 will break the css.
Here is a code I came Up with:
import React, {Component} from "react";
import './header.css'
export default class Header extends Component{
state = {
active : false
};
toggleMenuSwitch = () => {
this.setState((state)=>{
return{
active: !state.active
}
})
};
render() {
//destructuring
const {active} = this.state;
let className = 'toggle__sidebar';
if(active){
className += ' active';
}
return(
<header className="header">
<div className="header__wrapper">
<div className="header__cell header__cell--logo opened">
<a href="#" className="logo">
<img src="https://www.nrgcrm.olezzek.id.lv/images/logo.svg" alt=""/>
</a>
<a href="#" className={className}
onClick={ this.toggleMenuSwitch }
data-toggle="sidebar">
<i></i>
</a>
</div>
<div className="header__cell">
</div>
</div>
</header>
);
};
};
Just wanted to add my approach. Using hooks and context provider.
Nav.js
function NavBar() {
const filterDispatch = useDispatchFilter()
const {filter} = useStateFilter()
const activeRef = useRef(null)
const completeRef = useRef(null)
const cancelRef = useRef(null)
useEffect(() => {
let activeClass = '';
let completeClass = '';
let cancelClass = '';
if(filter === ACTIVE_ORDERS){
activeClass='is-active'
}else if ( filter === COMPLETE_ORDERS ){
completeClass='is-active'
}else if(filter === CANCEL_ORDERS ) {
cancelClass='is-active'
}
activeRef.current.className = activeClass
completeRef.current.className = completeClass
cancelRef.current.className = cancelClass
}, [filter])
return (
<div className="tabs is-centered">
<ul>
<li ref={activeRef}>
<button
className="button-base"
onClick={() => filterDispatch({type: 'FILTER_ACTIVE'})}
>
Active
</button>
</li>
<li ref={completeRef}>
<button
className="button-base"
onClick={() => filterDispatch({type: 'FILTER_COMPLETE'})}
>
Complete
</button>
</li>
<li ref={cancelRef}>
<button
className={'button-base'}
onClick={() => filterDispatch({type: 'FILTER_CANCEL'})}
>
Cancel
</button>
</li>
</ul>
</div>
)
}
export default NavBar
filterContext.js
export const ACTIVE_ORDERS = [
"pending",
"assigned",
"pickup",
"warning",
"arrived",
]
export const COMPLETE_ORDERS = ["complete"]
export const CANCEL_ORDERS = ["cancel"]
const FilterStateContext = createContext()
const FilterDispatchContext = createContext()
export const FilterProvider = ({ children }) => {
const [state, dispatch] = useReducer(FilterReducer, { filter: ACTIVE_ORDERS })
return (
<FilterStateContext.Provider value={state}>
<FilterDispatchContext.Provider value={dispatch}>
{children}
</FilterDispatchContext.Provider>
</FilterStateContext.Provider>
)
}
export const useStateFilter = () => {
const context = useContext(FilterStateContext)
if (context === undefined) {
throw new Error("place useStateMap within FilterProvider")
}
return context
}
export const useDispatchFilter = () => {
const context = useContext(FilterDispatchContext)
if (context === undefined) {
throw new Error("place useDispatchMap within FilterProvider")
}
return context
}
export const FilterReducer = (state, action) => {
switch (action.type) {
case "FILTER_ACTIVE":
return {
...state,
filter: ACTIVE_ORDERS,
}
case "FILTER_COMPLETE":
return {
...state,
filter: COMPLETE_ORDERS,
}
case "FILTER_CANCEL":
return {
...state,
filter: CANCEL_ORDERS,
}
}
return state
}
Works fast, and replaces redux.
const aDiv = useRef(null);
function app(){
const [isDark, setIsDark] = useState();
useEffect(()=>{
if(isDark){
aDiv.current.classList.add('dark-mode')
}else{
aDiv.current.classList.remove('dark-mode')
}
},[isDark]}
return <div className = "app" ref = {aDiv}> </div>
useRef to id the element to toggle the class, then a boolean useState to track switching, on true, we get the ref's current classList then add a className else we remove the className.
All this happen in the useEffect with our useState as dependency array.
import { useState } from "react";
import "./App.css";
export default function App() {
const [isActive, setIsActive] = useState(false);
const handleClick = (event) => {
// ️ toggle isActive state on click
setIsActive((current) => !current);
};
return (
<div>
<button className={isActive ? "bg-salmon" : ""} onClick={handleClick}>
Click
</button>
</div>
);
}

React - Functions are not valid as a React child

I am new to react this is my first application.
I am calling one component inside to another component then those function a moved to app.js
//app.js
class App extends React.Component {
state = {
todos:[
{id:1, title:'get haircut',completed: false},
{id:2, title:'learn react',completed: false},
{id:3, title:'chaaa',completed: false},
]
}
markComplete=(id) =>{
this.setState({
todos: this.state.todos.map((myTodo)=>{
if(myTodo.id === id ){
myTodo.completed = !myTodo.completed;
}
return myTodo
})
})
};
deleteTodo =(id) =>{
this.setState({
todos: [...this.state.todos.filter((myTodo) =>{
return myTodo !==id
})]
})
}
render(){
return (
<div className="App">
<Header/>
<RetrivedTodos todos={this.state.todos}
markComplete={this.markComplete}
deleteTodo={this.deleteTodo}
/>
</div>
);
}
}
//RetrivedTodos.js
class RetrivedTodos extends Component {
render () {
return this.props.todos.map((retrivedTodos) =>(
<TodosItems key={retrivedTodos.id} todos={retrivedTodos}
markComplete={this.props.markComplete}
deleteTodo={this.props.deleteTodo}
/>
))
}
}
//TodoItems.js
class TodosItems extends Component {
getStrikeMark = () => {
return {
textDecoration:this.props.todos.Completed ? 'line-through': 'none'
}
}
render () {
const { id , title } = this.props.todos
return (
<div className='main-todos-div' style={this.getStrikeMark()}>
<div className='todo-div'>
<input type="checkbox" className='checkbox-round'
onChange={this.props.markComplete.bind(this,id)}/>
<span>{title}</span>
</div>
<div className='btn-div'>
<button onClick={this.props.deleteTodo.bind(this,id)}>
<i className="fas fa-trash"></i>
</button>
</div>
</div>
)
}
}
//header
class Header extends Component {
render () {
const date= new Date();
const todayDate = date.getDate();
const month = date.toLocaleString('default',{month:'long'});
const year = date.getFullYear;
const day = date.toLocaleDateString('default',{weekday:'long'});
return(
<div className='main-header-div'>
<div className='background-div'> </div>
<div className='date-month-div'> </div>
<span>{todayDate}</span>
<span>{month}</span>
<span>{year}</span>
<span>{day}</span>
</div>
)
}
}
What is the problem here? It shows this error
Warning: Functions are not valid as a React child. This may happen if
you return a Component instead of from render. Or maybe
you meant to call this function rather than return it.
thanks in advance
Check the sandbox link:
https://codesandbox.io/s/affectionate-goodall-mh0t7?file=/src/Header.js
The problem is with Header componentnt, it should be :
const year = date.getFullYear();
instead of
const year = date.getFullYear;
getFullYear is a function, that's the reason you were getting the error.
RetrivedTodos seems invalid to me. You are returning a map function instead of a React component. This map function should be executed inside the return value instead of being the return value itself.
Here is how it should look like:
class RetrivedTodos extends Component {
render () {
return (
<div>
{this.props.todos.map((retrivedTodos) => (
<TodosItems key={retrivedTodos.id} todos={retrivedTodos}
markComplete={this.props.markComplete}
deleteTodo={this.props.deleteTodo}
/>
))
}
</div>
)
}
EDIT: Inside Header you are returning a function instead of it's value:
const year = date.getFullYear;
Should be:
const year = date.getFullYear();

Functional Component unable to render return value based on props values

Goal: To implement a Toast Message modal (using Functional Component) which will show or hide based on the props value (props.showToastModal) within the return of ToastModal component
Expected: Using props.showToastModal directly would determine if Toast appears
Actual: Modal does not appear based on props.showToastModal
Here's the code:
Parent Component
class Datasets extends Component {
constructor(props) {
super(props)
this.state = {
showToastModal: false,
toastModalText: ''
}
}
toggleOff = () => {
this.setState({ showToastModal: false, toastModalText: '' })
}
render() {
{this.state.showToastModal && (
<ToastModal
showToastModal={this.state.showToastModal}
toastModalText={this.state.toastModalText}
toggleOff={this.toggleOff}
/>
)}
}
}
Child Component
This works:
const ToastModal = (props) => {
const isOpen = props.showToastModal
return (
<div className={`${css.feedbackModal} ${isOpen ? css.show : css.hide}`}>
{props.toastModalText}
<i
className={`bx bx-x`}
onClick={() => props.toggleOff()}
/>
</div>
)
}
export default ToastModal
But this doesn't (using the props value directly):
const ToastModal = (props) => {
return (
<div className={`${css.feedbackModal} ${props.showToastModal ? css.show : css.hide}`}>
{props.toastModalText}
<i
className={`bx bx-x`}
onClick={() => props.toggleOff()}
/>
</div>
)
}
export default ToastModal
Using a const isOpen = props.showToastModal works as expected instead. I am confused why this happens. Is this is a React Lifecycle issue, or a case where it is bad practice to use props values which may be updated during the render?
Please try destructuring objects
const ToastModal = ({ showToastModal, toggleOff }) => {
return (
<div className={`${css.feedbackModal} ${showToastModal ? css.show : css.hide}`}>
{props.toastModalText}
<i
className={`bx bx-x`}
onClick={toggleOff}
/>
</div>
)
}
export default ToastModal

My search input and pagination aren't triggering anything in Reactjs

I'm fairly new to react.
My search input and pagination buttons aren't triggering anything and nothing comes up in the console, what is wrong with my code ?
I tried putting every functions in App.js to get it cleaner.
App.js
import React, { Component } from "react";
import List from './List';
let API = 'https://swapi.co/api/people/';
class App extends Component {
constructor(props) {
super(props);
this.state = {
results: [],
search: '',
currentPage: 1,
todosPerPage: 3
};
this.handleClick = this.handleClick.bind(this);
this.updateSearch = this.updateSearch.bind(this);
}
componentWillMount() {
this.fetchData();
}
fetchData = async () => {
const response = await fetch(API);
const json = await response.json();
this.setState({ results: json.results });
};
handleClick(event) {
this.setState({
currentPage: Number(event.target.id)
});
}
updateSearch(event) {
this.setState({
search: event.target.value.substr(0, 20)
});
}
render() {
return (
<div>
<List data={this.state} />
</div>
);
}
}
export default App;
List.js
import React, { Component } from 'react';
import Person from './Person';
class List extends Component {
render() {
const { data } = this.props;
const { results, search, updateSearch, handleClick, currentPage, todosPerPage } = data;
const indexOfLastTodo = currentPage * todosPerPage;
const indexOfFirstTodo = indexOfLastTodo - todosPerPage;
const currentTodos = results.slice(indexOfFirstTodo, indexOfLastTodo).filter(item => {
return item.name.toLowerCase().indexOf(search) !== -1;
});
const renderTodos = currentTodos.map((item, number) => {
return (
<Person item={item} key={number} />
);
});
const pageNumbers = [];
for (let i = 1; i <= Math.ceil(results.length / todosPerPage); i++) {
pageNumbers.push(i);
}
const renderPageNumbers = pageNumbers.map(number => {
return (
<li className="page-link" key={number} id={number} onClick={handleClick} style={{cursor: "pointer"}}>{number}</li>
);
});
return (
<div className="flex-grow-1">
<h1>Personnages de Star Wars</h1>
<form className="mb-4">
<div className="form-group">
<label>Rechercher</label>
<input
className="form-control"
type="text"
placeholder="luke skywalker..."
value={search}
onChange={updateSearch}
/>
</div>
</form>
<div className="row mb-5">{renderTodos}</div>
<nav aria-label="Navigation">
<ul id="page-number" className="pagination justify-content-center">{renderPageNumbers}</ul>
</nav>
</div>
);
}
}
export default List;
The value of the input doesn't change one bit if I type in it and if I right click on a page number, the console gets me Uncaught DOMException: Failed to execute 'querySelectorAll' on 'Element': '#4' is not a valid selector.
Any idea ?
The issue is that in the List class you attempt take updateSearch and handleClick out of data (which in turn comes from this.props). But updateSearch and handleClick are never placed inside data. If you log either of these methods to the console you'll see they are undefined.
To fix this, you need to pass updateSearch and handleClick from App to List. You can do this either by including the methods inside the data prop, or by passing them directly as their own props (which I would recommend).
For example, you can change the render method of App to look something like this:
render() {
return (
<div>
<List
data={this.state}
updateSearch={ this.updateSearch }
handleClick={ this.handleClick }
/>
</div>
);
}
Then in the render method of List you can do this:
const { data, updateSearch, handleClick } = this.props;
and remove the definitions of the two methods from the destructuring of data below.

How can I update the this.state.songs to songsList

I cant update the state songs which needs to get values from songsList . How can I update the songs to songsList ? Is it anything to do with the component life cycle ? While running the below code , 'songsList is undefined' error throws up . const songList is in the render .
import React, { Component } from 'react';
import logo from './components/Logo/box8.png';
import './App.css';
import SearchBox from './components/SearchBox/SearchBox';
import SongCards from './components/SongCards/SongCards';
import 'tachyons';
import axios from 'axios';
class App extends Component {
state = {
songs : [],
searchField: '',
entries: []
};
componentDidMount() {
axios.get(`https://itunes.apple.com/in/rss/topalbums/limit=100/json`)
.then(response =>
{this.setState({ entries: response.data.feed.entry });
});
}
onSearchChange=(event)=>{
this.setState({songs : songsList})
this.setState({searchField : event.target.value})
const filteredSongs = this.state.songs.filter(song =>{
return song.title.toLowerCase().includes(this.state.searchField.toLowerCase())
});
}
render(){
const songsList = this.state.entries.map(entries => {
return (
<SongCards
key={entries.id.label}
artist={entries["im:artist"].label}
image={entries["im:image"][2].label}
link={entries.id.label}
price={entries["im:price"].label}
date={entries["im:releaseDate"].label}
title={entries.title.label}
/>
);
});
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<SearchBox searchChange= {this.onSearchChange}/>
{songsList}
</div>
);
}
}
export default App;
Appreciate all your responses . I made it finally .
import React, { Component } from 'react';
import logo from './components/Logo/box8.png';
import './App.css';
import SearchBox from './components/SearchBox/SearchBox';
import Albums from './components/Albums/Albums';
import Scroll from './components/Scroll/Scroll';
import 'tachyons';
import emoji from 'emoji-dictionary';
import axios from 'axios';
class App extends Component {
state = {
show:false,
songs : [],
searchField: '',
};
componentDidMount() {
axios.get(`https://itunes.apple.com/in/rss/topalbums/limit=100/json`)
.then(response =>
{this.setState({songs:response.data.feed.entry });
});
}
itunesPageLoader=()=>{
this.setState({show:false})
}
onSearchChange=(event)=>{
this.setState({searchField : event.target.value})
}
render(){
const filteredSongs = this.state.songs.filter(song =>{
return
song.title.label.toLowerCase().includes(this.state.searchField.toLowerCase())
})
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<SearchBox searchChange= {this.onSearchChange}/>
<Scroll >
<Albums songs={filteredSongs}/>
</Scroll>
<footer className="pv4 ph3 ph5-m ph6-l red">
<small className="f6 db tc">© 2018 <b className="ttu">Box8 Inc</b>., All
Rights Reserved</small>
<div className="tc mt3">
{`Made with ${emoji.getUnicode("purple_heart")} by Renjith`}
</div>
</footer>
</div>
);
}
}
export default App;
Try this. You are actually assigning songsList to songs using setState but the songsList doesn’t exist in onSearchChange. To push searched value to an array you need to push event.target.value to songs array
Try with below corrected code
onSearchChange=(event)=>{
this.setState(prevState => ({songs : [...prevState.songs, event.target.value]}));
this.setState({searchField : event.target.value})
const filteredSongs = this.state.songs.filter(song =>{
return song.title.toLowerCase().includes(this.state.searchField.toLowerCase())
});
}
You have mentioned that this.state.entries is an Object.
If this is true, then yo can't perform .map on it as .map is an Array method.
You can however use Object.entries to get an array of [key,value] pairs of this.state.entries.
Object.entries(this.state.entries).map(([key,value]) => ...)
Simple running example:
const object1 = { foo: 'this is foo', baz: "this is baz" };
Object.entries(object1).map(([key,value]) => console.log(`key: ${key}, value: ${value}`));
So i will do something like this:
const IN_PROGRESS = 'IN_PROGRESS';
const SUCCESS = 'SUCCESS';
class App extends Component {
state = {
songs : null,
entries: null,
status: null
};
componentDidMount() {
this.setState({status: IN_PROGRESS});
axios.get(`https://itunes.apple.com/in/rss/topalbums/limit=100/json`)
.then({data} => {
const songs = data.feed.entry;
this.setState({entries: songs});
this.setState({songs});
this.setState({status: SUCCESS});
});
}
onSearchChange = ({target}) => {
const {value} = target;
const songs = this.state.entires.filter(song =>
song.title.toLowerCase().includes(value.toLowerCase())
});
this.setState({songs});
}
render() {
const {status, songs} = this.state;
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
</header>
<SearchBox searchChange={this.onSearchChange}/>
{
status === IN_PROGRESS &&
(/* you can insert here some kind of loader which indicates that data is loading*/)
}
{
status === SUCCESS && songs.map(entry => {
const {
id, ['im:artist']: artist, ['im:image']: image,
['im:price']: price, ['im:releaseDate']: date, title
} = entry;
return (
<SongCard
key={id.label}
artist={artist.label}
image={image[2].label}
link={id.label}
price={price.label}
date={date.label}
title={entry.title.label}
/>
)
}
}
{
//Here you can display error message if status === FAILURE
}
</div>
);
}
}
When component did mount, I set status into IN_PROGRESS (if you want some kind of loader to show), and data are beeing fetched - axios.get is asynchronous so remember that when data is fetching then render method is already triggered. When data is loaded then in state I hold two variables, entries which holds unfiltered list of songs, and songs which holds filteres songs.
When search is triggered then I filter entires by searched phrase and set into state this filtered array.
Component renders songCards mapping by filtered songs

Resources