Passing data into another component - reactjs

I'm trying to pass data into another component, but I'm having trouble re-rendering state so it's not a blank array when it passes the component.
Movie Card Component:
import React, { Component } from 'react';
import getMovies from './MovieAPI.js';
import MoviePoster from './MoviePoster.js';
class MovieCardII extends Component {
constructor() {
super();
this.state = {
movies: [],
};
}
componentDidMount() {
getMovies().then(results => {
this.setState(results.Search: movies)
console.log("state", this.state);
console.log(results.Search);
});
}
render() {
const { movies } = this.state;
return (
<div>
<h1> Hi </h1>
<MoviePoster movies={movies} />
</div>
);
}
}
export default MovieCardII;
MoviePoster Component
import React from 'react';
import PropTypes from 'prop-types';
const MoviePoster = props => {
const { movies } = props;
console.log(movies);
return (
<div>
{movies.map(movie => (
<div>
<h1> {movie.Poster} </h1>
</div>
))}
</div>
);
};
MoviePoster.propTypes = {
movies: PropTypes.array.isRequired,
};
export default MoviePoster;
I'm using the OMDB API and the MovieCard component is able to get a state with an array list of 10 once I am able to get the get request in.
But in the MoviePoster component, the movie array remains an empty array.
New to React so I'm struggling to understand how to pass data into another component. I am able to get the create the view I want if I don't have to pass data to another array, but I need to create one since the API i'm using is not able to get all the information I need to make another API request using the movie ID in another component later. So basically the set up will be
movieCard is the parent
movie poster, and movieInfo will be the children.
The movieInfo will pull another API request using the imdbID that I get from the movieCard component.

The way you have set the state is wrong
Movie Card Component:
componentDidMount(){
getMovies().then(results=> {
this.setState(results.Search: movies)
console.log("state", this.state);
console.log(results.Search);
});
}
Solution: Movie Card Component
componentDidMount(){
getMovies().then(results=> {
this.setState({movies: results.Search})
console.log("state", this.state);
console.log(results.Search);
});
}
One more change is required to MoviePoster Component
You need to specify key whenever you are looping
const MoviePoster = (props) => {
const { movies } = props;
console.log(movies);
return (
<div>
{movies.map(movie => (
<div key={movies.id}>
<h1> {movie.Poster}</h1>
</div>
))}
</div>
)
}
Hope this solution will help you.

Related

How to properly mitigate invalid hook issues in reactjs

Am trying to display my records from airtable.com using reactjs but it throws error
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
I have reference solution found here
link
I change the Rec class to function and then try to export it but still cannot get it to work
here is the code
import {initializeBlock, useBase, useRecords} from '#airtable/blocks/ui';
import React, { Component } from "react";
import ReactDOM from 'react-dom';
class Rec extends React.Component{
constructor(props) {
super(props);
this.state = {
id: ''
};
}
myRecords() {
alert('ok');
const base = useBase();
const table = base.getTableByNameIfExists('myfirst_table');
// grab all the records from that table
const records = useRecords(table);
// render a list of records:
return (
<ul>
{records.map(record => {
return <li key={record.id}>{record.id} </li>
})}
</ul>
);
render() {
return (
<div>
<h2>My Records</h2>
{this.myRecords()}
</div>
);
}
}
export default Rec;
UPDATED SECTION WITH SOLUTION BY MR. HAGAI
class Rec extends React.Component{
constructor(props) {
super(props);
this.state = {
id: ''
};
}
myRecords() {
alert('ok');
//const base = useBase();
//const table = base.getTableByNameIfExists('myfirst_table');
// grab all the records from that table
//const records = useRecords(table);
// render a list of records:
return (
<ul>
{records.map(record => {
return <li key={record.id}>{record.id} </li>
})}
</ul>
);
}
render() {
return (
<div>
<h2>Hello welcome to contact page</h2>
{this.myRecords()}
</div>
);
}
}
export default () => {
const base = useBase();
const table = base.getTableByNameIfExists('myfirst_table');
const records = useRecords(table);
return <Rec base={base} records={records} />
}
//export default Rec;
useBase and useRecords hooks can't be called inside a class component, but there is a little workaround you can do for not re-write code by export arrow function that will pass base and records as props
class Rec extends React.Component{
...rest class without useBaes() and useRecords() assiganing...
}
export default (props) => {
const base = useBase();
const table = base.getTableByNameIfExists('myfirst_table');
const records = useRecords(table);
return <Rec {...props} base={base} recoreds={records} />
}
now base available at this.props.base and no hook called inside a class component

How to use map on multi objects array in React

This is child component as i can you Props here
Child Component:
import React from "react";
const PeopleList = props => {
console.log("child Props :", props.data);
const list = props.data.map(item => item.name);
return <React.Fragment>{"list"}</React.Fragment>;
};
export default PeopleList;
Main Component:
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchPeople } from "../actions/peopleaction";
import PeopleName from "../containers/peopleName";
class Main extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.dispatch(fetchPeople());
}
render() {
const { Error, peoples } = this.props;
console.log("data", peoples);
return (
<div className="main">
{"helo"}
<PeopleName data={peoples.results} />
</div>
);
}
}
const mapStateToProps = state => {
return {
peoples: state.peoples.peoples,
error: state.peoples.error
};
};
export default connect(mapStateToProps)(Main);
If i iterate the props multi objects array i can face Map is not define issue;
I need to iterate the props.data multi objects array in child component and i get object from Redux store. once component loaded the redux store.
can you please some one help me on this.
you can find whole code below mentioned
Try this It works in your codesandbox.
{peoples.results && <PeopleName data={peoples.results} />}

React Context not passing data to its child components

I am a beginner in React. Looking at a few medium articles and React docs(which is complicated) I have tried to implement this very basic Context API.
I have missed some basic point which is the reason why I haven't got the correct result which is to pass data through the components tree and access them in the child component.
Please let me know how to correct given code snippet and what have I missed.
import React from 'react';
import './index.css';
const AppContext = React.createContext();
function GreenBox () {
return <div className='green-box'>
<AppContext.Consumer>
{(context) => context.value}
</AppContext.Consumer>
</div>
}
function BlueBox () {
return <div className='blue-box'><GreenBox/></div>
}
class RedBox extends React.Component {
render() {
return <div className='red-box'>
<AppContext.Consumer>
{(context) => context.value}
</AppContext.Consumer>
<BlueBox/>
</div>
}
}
class Context extends React.Component {
state = {
number: 10
}
render() {
return (
<AppContext.Provider value = {this.state.number}>
<RedBox/>
</AppContext.Provider>
)
}
}
export default Context;
The value you set in the Provider will be the argument received in the render props function in Consumer, so instead of accessing the number you're expecting with context.value, you should just change to context.

write to local state from props

I understand that the problem is rather trivial, but I can't deal with it, I need your help.
I tried all the solutions in similar questions, but it did not work for me
The bottom line is that when I mount the component, I run fetch and I get a list of articles from my API, but this does not suit me, since I don’t save them in the local state.
Besides, my terrible knowledge of React, I have 2 more problems:
1) When I navigate through the pages, when I return to the articles page, the number of results is duplicated in an arithmetic progression, as I understand it, this is the problem that I keep articles in props, but I need to save it in a local state.
2) From this my second problem expires. I tried everything, but I could not do props.articles -> state.articles, in order to apply this.state.articles.map in the future
//actions
import {FETCH_ALL_ARTICLES} from "../constants";
export const fetchAllArticles = () => {
return (dispatch) => {
let headers = {"Content-Type": "application/json"};
return fetch("/api/articles/", {headers, })
.then(res => {
if (res.status < 500) {
return res.json().then(data => {
return {status: res.status, data};
})
} else {
console.log("Server Error!");
throw res;
}
})
.then(res => {
if (res.status === 200) {
return dispatch({type: FETCH_ALL_ARTICLES, articles: res.data});
}
})
}
};
//component
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import {articles} from "../actions";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
articles: []
}
console.log(this.props.articles)
};
componentDidMount() {
this.props.fetchAllArticles()
};
render() {
return (
<div>
<Link to='/notes'>Notes</Link>
<h2>All articles</h2>
<hr />
<table>
<tbody>
{this.state.articles.map((article, id) => (
<tr key={`article_${id}`}>
<td>{article.headline}</td>
<td>{article.description}</td>
<td>{article.created}</td>
<td>{article.author.username}</td>
<td>{article.image}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
}
const mapStateToProps = state => {
return {
articles: state.articles,
}
};
const mapDispatchToProps = dispatch => {
return {
fetchAllArticles: () => {
dispatch(articles.fetchAllArticles())
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
// reducer
import {FETCH_ALL_ARTICLES} from "../constants";
const initialState = [];
export default function articles(state=initialState, action) {
switch (action.type) {
case FETCH_ALL_ARTICLES:
return [...state, ...action.articles];
default:
return state;
}
}
Your question is unclear but I will try to explain based on the title 'write to local state from props'.
You can utilize component lifecycles as below to achieve that
componentWillReceiveProps(nextProps) {
if (nextProps.articles) {
this.setState({ articles: nextProps.articles });
}
}
Basically whenever there is an update to this component, this lifecycle method componentWillReceiveProps will get invoked before re-rendering, so we can call setState here and save it to local state.
when I return to the articles page, the number of results is duplicated in an arithmetic progression
This should not happened if you handle your reducer correctly. For example, after you fetch articles from API, clear your array then only store the value you receive from API. But then of course it's all depending on what you want to achieve
Every time your component mounts, you fetch all the articles.
When you fetch all the articles, you add them to your existing Redux state:
return [...state, ...action.articles];
To fix this, you can discard the old articles instead of keeping them:
return [...action.articles];
Or you can avoid fetching articles if they have already been fetched:
componentDidMount() {
if (!this.props.articles || this.props.articles.length === 0) {
this.props.fetchAllArticles()
}
};
You don't need to do anything with local state. Your Redux state is your single source of truth. Keeping another copy of the data in local state serves no purpose.
You can render your articles directly from this.prop.articles in render function.
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {Link} from 'react-router-dom';
import {articles} from "../actions";
class Home extends Component {
componentDidMount() {
this.props.fetchAllArticles()
};
render() {
return (
<div>
<Link to='/notes'>Notes</Link>
<h2>All articles</h2>
<hr />
<table>
<tbody>
{this.props.articles.map((article, id) => (
<tr key={`article_${id}`}>
<td>{article.headline}</td>
<td>{article.description}</td>
<td>{article.created}</td>
<td>{article.author.username}</td>
<td>{article.image}</td>
</tr>
))}
</tbody>
</table>
</div>
)
}
}
const mapStateToProps = state => {
return {
articles: state.articles,
}
};
const mapDispatchToProps = dispatch => {
return {
fetchAllArticles: () => {
dispatch(articles.fetchAllArticles())
}
}
};
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Your question is short, but as I understand you are looking to pass the data through props from parent component to child component and then you want to store them into local state.
For that, you need to add constructor into child component and assigned props to state like.
import React,{Component} from 'react';
class ChildComponent extends Component{
constructor(props){
super(props);
this.state={
name: props.name,
email: props.email
}
}
..........
// your child component logic
}
Pass the data through parent component like,
<ChildComponent name={this.state.name} email={this.state.email} />

Why is the Object data not displayed in the browser when used inside JSX?

I have a React Container that is connected to Redux Store. My Redux Store has an array of data which i consume in mapStateToProps. But I am unable to use it inside JSX. There is no error. However, Nothing is displayed in the browser. Console logging the object gives the properties.
import React from 'react';
import {connect} from 'react-redux';
class CurrentStore extends React.Component {
render () {
console.log(this.props.current);
return (
<div className='centered row'>
<div className='column'>
{this.props.current.name}
</div>
</div>
);
}
}
function mapStateToProps (state, ownProps) {
return {
current: state.app.stores.filter(s => s._id === ownProps.match.params.storeId)
}
}
export default connect(mapStateToProps)(CurrentStore);
Array.prototype.filter gives you an array of values back, which means that your current object is in fact an array of objects.
So either, you should change your mapStateToProps to return the first value, like:
function mapStateToProps (state, ownProps) {
return {
current: state.app.stores.filter(s => s._id === ownProps.match.params.storeId)[0]
}
}
, or you should do it in the render function (which looks lots more appropriate)
class CurrentStore extends React.Component {
render () {
console.log(this.props.current);
return (
<div className='centered row'>
{ this.props.current && this.props.current.map( item => <div className='column'>{ item.name }</div>}
</div>
);
}
}
Inside the render it is now possible that more than 1 item would be returned, in case you don't want that, you could change the render to take only the first item
class CurrentStore extends React.Component {
render () {
let { current } = this.props;
if (!current || !current[0]) {
return null;
}
return (
<div className='centered row'>
<div className='column'>{ current[0].name }</div>
</div>
);
}
}

Resources