React - Functions are not valid as a React child - reactjs

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();

Related

I am not able to see the value updated that I insert manually

I am trying to implement an onChange method that when the user type something it gets updated in real time and displayed in the div. The component that I am talking about is at the end of the code and it's called and it is an input that will be rendered 4 times on the dom. For a reason no value get shown on the div I mean {this.state.stake}. Could anyone help me in fixing that? Thanks
import React, { Component } from 'react';
import Stake from './stake';
class FetchRandomBet extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
bet: null,
value: this.props.value,
stake: ''
};
}
async componentDidMount() {
const url = "http://localhost:4000/";
const response = await fetch(url);
const data = await response.json();
this.setState({
loading: false,
bet: data.bets,
});
}
changeStake = (e) => {
this.setState({
stake: [e.target.value]
})
}
render() {
const { valueProp: value } = this.props;
const { bet, loading } = this.state;
if (loading) {
return <div>loading..</div>;
}
if (!bet) {
return <div>did not get data</div>;
}
return (
< div >
{
loading || !bet ? (
<div>loading..</div>
) : value === 0 ? (
<div className="bet-list">
<ol>
<p>NAME</p>
{
bet.map(post => (
<li key={post.id}>
{post.name}
</li>
))
}
</ol>
<ul>
<p>ODDS</p>
{
bet.map(post => (
<li key={post.id}>
{post.odds[4].oddsDecimal}
<div className="stake-margin">
<Stake
onChange={this.changeStake} />
{this.state.stake}
</div>
</li>
))
}
</ul>
</div>
Pass this.state.stake as a prop of Stake component.
<Stake
onChange={this.changeStake}
stake={this.state.stake}
/>
Then inside of the Stake component assign stake prop to value on an the input. It would look something like this.
const Stake =({stake, onChange})=>{
return <input value={stake} onChange={onChange} />
}

How to render a stateless functional component from another component

I'm new on React. I wrote a project on which there is a search component. the search works fine ( I checked on console.log) but I don't know how to call the stateless function component on which the search results should be shown?
class SearchCard extends Component {
// qQuery is a variable for query input
state = { qQuery: "" };
HandleSearch= async (e) => {
e.preventDefault();
const {data:cards} = await cardService.getAllCards();
var searchResults = cards.filter((item) =>
item.qTopic.includes(this.state.qQuery) ||
item.qArticle.includes(this.state.qQuery)
);
this.setState({ cards : searchResults });
// console.log('search results ',searchResults, ' cards ',this.state);
return <CardRender cards={cards}/>
}
render() {
return (
<React.Fragment>
<form className="form" onSubmit={ this.HandleSearch }>
<div className="input-group md-form form-sm form-1 pl-4 col-12">
const CardRender = ({cards,favs,onHandleFavs}) => {
return (
<div className="row">
{cards.length > 0 &&
cards.map((card) =>
<Card key={card._id}
card={card}
favs={favs}
onHandleFavs={() => onHandleFavs(card._id)}
/>
}
</div>
);
}
export default CardRender;
screenshot
You should add the <CardRender cards={cards}/> to the element render returns (at the place you want it to be) and render it if state.cards is not empty.
Something like this
class SearchCard extends Component {
// qQuery is a variable for query input
state = { qQuery: "" };
HandleSearch= async (e) => {
// ...
this.setState({ cards : searchResults });
}
render() {
return (
<div>
...
{cards?.length && <CardRender cards={cards}/>}
</div>
);
}
}

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.

React filter 'state' undefined

So I started using reactjs and I've managed to loop through some XML data but having issues adding a search/filter into it.
This is my code so far:
import React, { Component } from 'react';
import XMLMapping from 'xml-mapping';
import axios from 'axios';
class Guests extends Component {
constructor(props) {
super(props);
this.state = {
guests: [],
search: 'Search Guests'
};
}
componentDidMount() {
axios.get('http://localhost:8080/guestlist.xml')
.then(res => {
const xml = XMLMapping.load(res.data);
var guests = XMLMapping.tojson(xml);
this.setState({guests: guests});
//console.log(guests);
return guests;
});
}
updateSearch(event) {
this.setState({
// Limit to 10 characters only for search
search: event.target.value.substr(0, 10)
});
// console.log(this.state.search); // this will show the previous value of state.
}
render() {
function mapObject(object, callback) {
return Object.keys(object).map(function (key) {
return callback(key, object[key]);
});
}
const firstname = mapObject(this.state.guests, function (key, value) {
return <div key={key}>
{value.record
.map((item,index) => {
//console.log(index)
return <div className="columns" key={index}>
<div className="column" key={index}>{item.first_name.$t} {item.last_name.$t}</div>
<div className="column" >{item.company.$t}</div>
</div>;
})}
</div>
});
let filteredGuests = mapObject(this.state.guests, function (key, value) {
value.record.filter(
(contact) => {
return contact.first_name.$t.indexOf(this.state.search) !== -1;
//console.log(this.state.search)
}
);
});
return (
<div>
<div className="container">
<section className="section">
<h1 className="title">Guests attending Event A</h1> <input className="text" type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<div className="columns"><div className="column">Name</div><div className="column">Company</div></div>
{firstname}
</section>
</div>
</div>
);
}
}
export default Guests;
But it seems to be throwing an error TypeError: Cannot read property 'state' of undefined
This is the line return contact.first_name.$t.indexOf(this.state.search) !== -1;
Any advice or feedback would be appreciate!
As Prakash sharma wrote, you used the wrong function context.
Try to replace function with arrow function:
let filteredGuests = mapObject(this.state.guests, (key, value) => {
value.record.filter(
(contact) => {
return contact.first_name.$t.indexOf(this.state.search) !== -1;
//console.log(this.state.search)
}
);
})

State not updating in Component

Hey I am trying to create a simple to-do list and I have added the components necessary. However, the state is not being updated in the Title {this.state.data.length} and the TodoList {this.state.data}. A Codepen and the relevant code is below.
https://codepen.io/skasliwal12/pen/BREYXK
const TodoForm = ({addTodo}) => {
let input;
return (
<div>
<input ref={node => {input = node;}} />
<button onClick={(e) => {
e.preventDefault();
addTodo(input.value);
input.value='';
}}> +
</button>
</div>
);
};
const TodoList = ({todos}) => {
let todoNodes = todos.map(todo => {
return <li>{todo}</li>
});
return <div> {todoNodes} </div>;
}
const Title = ({todoCount}) => {
return (
<div>
<div>
<h1>To-do App {todoCount} items</h1>
</div>
</div>
);
}
class TestApp extends React.Component {
constructor(props) {
super(props);
this.state = { data : [] }
}
addTodo(val) {
let todo = {text: val}
this.state.data.push(todo);
this.setState = ({data: this.state.data});
console.log('state updated?')
}
render(){
return (
<div>
<Title todoCount={this.state.data.length}/>
<TodoForm addTodo={this.addTodo.bind(this)}/>
<TodoList todos={this.state.data}/>
</div>
);
}
}
ReactDOM.render(<TestApp />, document.getElementById('root'));
Quite simply it is important that you DO NOT MUTATE the state like you are doing here
this.state.data.push(todo);
It is hard to debug and adds side effects that are hard to keep track of. Following your approach you should copy the state to a var, update that var and then pass it as the new field in your state. Which could work but it's also something I do not recommend. A general good approach is to to compute the new state based on the old one
// this.state.data.push(todo); You can remove this line
this.setState(prevState => ({ data: prevState.data.concat(todo) }))
This will fix your issue and avoid mutating the state, which is something you should never do, only update the state using the setState method.
I also updated your TodoList which was not displaying properly, you have to access the text field of the todo in order to show something.
const TodoList = ({todos}) => {
let todoNodes = todos.map(todo => {
return <li>{todo.text}</li>
});
return <div> {todoNodes} </div>;
}
https://codepen.io/anon/pen/MmRVmX?editors=1010

Resources