Good day! I wanna to do dynamic dropdown menu, witch get data from server. So, if menu item clicked i wanna to fetch data from server and render result in list. Like this:
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {fetchData} from 'actions'
class MenuItem extends Component {
constructor(props) {
super(props);
this.state = {
isOpened: false,
}
}
toggleOpen() {
this.props.fetchData(); // Here is fetching data using redux-thunk
this.setState({isOpened: !this.state.isOpened});
}
render() {
const {data} = this.props;
return (
<a href="#" className="left-menu__link"
{...( this.state.isOpened && {
className: "left-menu__link is-opened"
} )}
onClick={(e) => {
e.preventDefault();
this.toggleOpen.bind(this)();
}}>
<ul className="left-menu__sub-level">
{!R.isEmpty(data) && data.map((item) =>
<li item={item} key={item.id}/>)}
</ul>
)
}
const mapStateToProps = state => ({
data: getData(state), // get data from state
});
const mapDispatchToProps = {
fetchData
};
export default connect(mapStateToProps, mapDispatchToProps)(MenuItem)
So can you tell me which is the best way to do this?
Here I have changed some of the code. I hope it will help
import React, {Component} from 'react'
import {connect} from 'react-redux'
import {fetchData} from 'actions'
class MenuItem extends Component {
constructor(props) {
super(props);
this.state = {
isOpened: false
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
e.preventDefault()
this.setState({isOpened: !this.state.isOpened})
this.props.fetchData().then((response) => {
....
....
})
}
render() {
const {data} = this.props;
const className = !this.state.isOpened ? 'left-menu__link' : 'left-menu__link is-opened'
return (
<a href="#" className={className}
onClick={this.handleClick}>
<ul className="left-menu__sub-level">
{!R.isEmpty(data) && data.map((item) =>
<li item={item} key={item.id}/>)}
</ul>
)
}
}
const mapStateToProps = state => ({
data: getData(state), // get data from state
});
const mapDispatchToProps = {
fetchData
};
export default connect(mapStateToProps, mapDispatchToProps)(MenuItem)
Related
I am fairly new to testing React applications with Jest and Enzyme. I am trying to test some connected components, but don't understand how to mock data for those. I have an API call and use props. Here are my files:
News Page
import React, { Component } from 'react';
import './news.style.css';
import NewsList from '../../components/news-list/news-list.component';
import { SearchBar } from '../../components/search-bar/search-bar.component';
import Header from '../../components/header/header.component';
import { NewsFooter } from '../../components/news-footer/news-footer.component';
class News extends Component {
constructor() {
super();
this.state = {
news: [],
searchField: '',
topics: ''
};
}
componentDidMount() {
fetch('https://sheltered-earth-94987.herokuapp.com/news')
.then(response => response.json())
.then(news => this.setState({ news: news}));
}
render() {
const { news, searchField, topics } = this.state;
const filteredNewsname = news
.filter(news => news.news_topic.toLowerCase().includes(topics.toLowerCase()))
.filter(news => news.news_headline.toLowerCase().includes(searchField.toLowerCase()));
return (
<div>
<Header/>
<h1 className="pageheadline">News</h1>
<SearchBar
placeholder='Search News'
handleChange= {e => this.setState({ searchField: e.target.value})}
/>
<div className="newslist">
<NewsList news={filteredNewsname}>
</NewsList>
</div>
<div className="newsfooter">
<NewsFooter
handleClick= {e => this.setState({ topics: e.target.id})}
/>
</div>
</div>
);
}
}
export default News;
NewsList Component
import React from 'react';
import './news-list.style.css';
import { NewsCard } from '../news-card/news-card.component';
import { Link } from 'react-router-dom';
const NewsList = props => {
return <div className='news-list'>
{
props.news.map(newsentry => <Link to={`/news/${newsentry.news_id}`}>
<NewsCard key={newsentry.news_id} newsentry={newsentry}/></Link>)
}
</div>;
};
export default NewsList;
NewsCard Component
import React from 'react';
import './news-card.style.css';
const NewsCard = props => (
<div className='news-card-container' data-test="news-card-container">
<img className="newsimg" alt="Newsimage" src={ props.newsentry.news_header_image}></img>
<div className="newsinfo">
<h4 className="newstitle"> { props.newsentry.news_headline } </h4>
<p className="teaser">{props.newsentry.news_teaser}</p>
<p className="author">By {props.newsentry.news_author} </p>
</div>
<p className="newstopic">#{props.newsentry.news_topic}</p>
</div>
)
export default NewsCard;
How can I test the NewsList and the NewsCard Components with mocked data?
This is how I started:
Testfile
import { shallow } from 'enzyme';
import React from 'react';
import NewsCard from './news-card.component';
import { findByTestAttr } from '../../../utils/index';
const setUp = (props={}) => {
const component = shallow(<NewsCard {... props}/>);
return component;
}
describe('NewsCard Component', () => {
describe('Have props', () => {
let wrapper;
beforeEach(() => {
const props = {
news: [],
};
wrapper = setUp(props);
});
it('Should render without errors', async () => {
const component = findByTestAttr(wrapper, 'news-card-container');
expect(component.length).toBe(1);
})
});
})
File with findByTestAttr function
export const findByTestAttr = (component, attr) => {
const wrapper = component.find(`[data-test='${attr}']`);
return wrapper;
}
For this right now I get an error, saying:
TypeError: Cannot read properties of undefined (reading 'news_header_image')
Before rendering components in test block; you can provide a new, mockup variable to your component.
Example:
<NewsCard key={..} newsentry={mockupNewsentry}/> with mockupNewsentry being your mockup data variable.
Longer example:
test("renders singleitem with data", async () => {
const mockupData = {
name: "Homer Simpson",
job: "Nuclear Safety Inspector",
id: "14",
};
render(
<SingleItem data={mockupData} />
);
const element = await screen.findByText(/Homer Simpson/i);
expect(element).toBeInTheDocument();
});
Check out this package. It will mock the network layer. Everyone is using this one for integration testing of components.
https://mswjs.io
I am trying to display recipes and not sure if I have this setup correctly. I am pulling recipes from a rails api via get fetch request. At the moment nothing is displaying.
Here is my recipe container:
import React, { Component } from 'react'
import RecipeList from '../components/RecipeList'
import RecipeInput from '../components/RecipeInput'
import { connect } from 'react-redux'
import { postRecipes } from '../actions/postRecipes.js'
import { getRecipes } from '../actions/getRecipes'
class RecipeContainer extends Component{
constructor(props){
super(props)
}
componentDidMount(){
getRecipes()
}
render(){
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} />
</div>
)
}
}
const mapStateToProps = state =>({
recipes: state.recipes
})
const mapDispatchToProps = dispatch =>{
return{
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
// deleteRecipe: id => dispatch({type: 'Delete_Recipe', id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipeContainer)
Here is my get request....notice that I am returning my Recipe component here.
export const getRecipes = () => {
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
dispatch({ type: 'START_FETCHING_RECIPES_REQUEST' });
fetch(RECIPES_URL)
.then(response =>{ return response.json()})
.then(recipes => dispatch({ type: 'Get_Recipes', recipes }));
};
}
This is where I am trying to render the Recipe component from the get request
import React, {Component} from 'react';
// import { getRecipes } from '../actions/getRecipes.js';
import Recipe from './Recipe.js'
class RecipeList extends Component {
// componentDidMount(){
// getRecipes()
// }
render() {
return (
<div>
{this.props.recipes.map(recipe => (<Recipe recipe={recipe} key={recipe.id} />))}
</div>
)
}
}
export default RecipeList;
Edit: Added reducer
switch(action.type){
case 'Add_Recipe':
const recipe = {
name: action.name,
ingredients: action.ingredients,
chef_name: action.chef_name,
origin: action.origin,
category: action.category
}
return{
...state,
recipes: [...state.recipes, recipe],
}
case 'START_FETCHING_RECIPES_REQUEST':
return {
...state,
recipes: [...state.recipes],
requesting: true
}
case 'Get_Recipes':
return {
...state, recipes: action.recipes,
requesting: false
}
default:
return state
}
}
How can I correct this to make it work?
Issue
You are not passing the recipes to the RecipeList component that were fetched and presumably stored in state, and fed back to the UI via RecipeContainer.
Solution
Pass the recipe state from RecipeContainer to RecipeList as a prop. and then render/map the recipes from props.
RecipeContainer
class RecipeContainer extends Component{
componentDidMount() {
getRecipes();
}
render() {
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList getRecipes={this.props.recipes} /> // <-- pass recipe state
</div>
)
}
}
const mapStateToProps = state => ({
recipes: state.recipes,
});
const mapDispatchToProps = dispatch => {
return {
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
}
};
RecipeList
class RecipeList extends Component {
render() {
const { recipes } = this.props;
return (
<div>
{recipes.map(recipe => (
<Recipe recipe={recipe} key={recipe.id} />
))}
</div>
);
}
}
The actual solution to this was I needed to have an explicit return in my mapStateToProp function.
Eg.
const mapStateToProp = state =>{
return {
recipes: state.recipes
}
}
currently working on adding the items to cart using react and redux but the add item does not work
I'm taking the items from my collections page and then passing the key to the product preview page
I'm using react-redux cartReducer the three files are
just can't figure out how to pass the fish products
product page
cart actions
cart reducer
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import firebase from '../../firebase/firebase';
import { connect } from 'react-redux';
import { addItem } from '../../redux/cart/cart-actions'
class FishPage extends Component {
constructor(props) {
super(props);
this.ref = firebase.firestore().collection('fishproducts');
this.unsubscribe = null;
this.state = {
fishproducts: []
};
}
componentDidMount() {
const ref = firebase.firestore().collection('fishproducts').doc(this.props.match.params.id);
ref.get().then((doc) => {
if (doc.exists) {
this.setState({
fishproducts: doc.data(),
key: doc.id,
isLoading: false
});
} else {
console.log("No such document!");
}
});
}
render() {
return (
<div >
<div>
<div>
<h4><Link to="/">back</Link></h4>
<h3>
{this.state.fishproducts.name}
</h3>
</div>
<div >
<dl>
<dt>Description:</dt>
<dd>{this.state.fishproducts.description}</dd>
<dt>Discount:</dt>
<dd>{this.state.fishproducts.discount}</dd>
<dt>Size:</dt>
<dd>{this.state.fishproducts.size}</dd>
<dt>Weight:</dt>
<dd>{this.state.fishproducts.weight}</dd>
<dt>Price:</dt>
<dd>{this.state.fishproducts.price}</dd>
<dt>Stock:</dt>
<dd>{this.state.fishproducts.stock}</dd>
</dl>
<button onClick={() => addItem(this.state.fishproducts)} >ADD TO CART</button>
</div>
</div>
</div>
);
}
}
const mapDispatchToProps = dispatch => ({
addItem: item => dispatch(addItem(item))
})
export default connect(null, mapDispatchToProps)(FishPage);```
this is cart action page
```import CartActionTypes from './cart-types';
export const toggleCartHidden = () => ({
type:CartActionTypes.TOGGLE_CART_HIDDEN
});
export const addItem = item => ({
type: CartActionTypes.ADD_ITEM,
payload: item
})```
this is cart reducer
```import CartActionTypes from './cart-types';
const INITIAL_STATE = {
hidden: true,
cartItems: []
};
export const cartReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case CartActionTypes.TOGGLE_CART_HIDDEN:
return {
...state,
hidden: !state.hidden
};
case CartActionTypes.ADD_ITEM:
return {
...state,
//cartItems: addItem(state.cartItems, action.payload)
cartItems: [...state.cartItems,action.payload]
};
default:
return state;
}
}
export default cartReducer;```
cant figure out how to pass fishproducts
So concept of React is that you need to access Firebase with a function. For that you should use a functional component.
React allows Hooks to get access to your state without a constructor so that's all
and then you'll need to use dispatch.
import React, { useState, useEffect } from 'react';
import firebase from '../../firebase/firebase';
import { Link } from 'react-router-dom';
import { connect , useDispatch} from "react-redux";
import { addItem} from '../../redux/cart/cart-actions';
const FishPage = (props) => {
const [state, setState] = useState({
name: '',
… rest of the values
isLoading: true,
})
const { name, … rest of the values } = state;
useEffect(() => {
setState({ isLoading: true });
const ref = firebase.firestore().collection('fishproducts').doc(props.match.params.id);
ref.get().then((doc) => {
setState({
name: doc.data().name,
… rest of the values
isLoading: false,
});
})
}, [props.match.params.id])
const item = [];
const dispatch = useDispatch();
return (
<div >
<div>
//your body here
<button onClick={() => dispatch(addItem(item))} >ADD TO CART</button>
</div>
</div>
</div>
);
}
const mapDispatchToProps = dispatch => {
return{
addItem: (item) => dispatch(addItem(item))
}
}
export default connect(null, mapDispatchToProps)(FishPage)
I am new to React and this threw up while trying to map some data from API. I have set the state to an array but still this error comes up.
import React, { Component } from 'react';
import axios from 'axios';
class Test extends Component {
state = {
articles: [],
}
componentDidMount() {
axios.get('https://newsapi.org/v2/top-headlines?country=us&apiKey=ef678d80cc70495184c2bf95d4576c9b')
.then(response => {
const articles = response.data;
this.setState({ articles });
})
}
render() {
return (
<div>
<ul>
{this.state.articles.map(article => <li><a href={`${article.url}`}>{article.title}</a></li>)}
</ul>
</div>
)
}
}
export default Test;
Try changing
const articles = response.data;
to
const articles = response.data.articles;
Its because the api returns an JSON Output with response in articles key.
import React, {Component} from 'react';
import axios from 'axios';
class Test extends Component {
state = {
articles: [],
}
componentDidMount() {
axios.get('https://newsapi.org/v2/top-headlines?country=us&apiKey=ef678d80cc70495184c2bf95d4576c9b')
.then(response => {
const articles = response;
this.setState({articles:articles.articles});
}) }
render() {
return (
<div>
<ul>
{this.state.articles.map(article =>
<li><a href={`${article.url}`}>{article.title}</a>
</li>)}
</ul>
</div>
) }
}
export default Test;
import React, {Component} from 'react'; import axios from 'axios';
class Test extends Component {
state = {
articles: [],
flag:false
}
componentDidMount() {
axios.get('https://newsapi.org/v2/top-headlines?country=us&apiKey=ef678d80cc70495184c2bf95d4576c9b')
.then(response => {
const articles = response.data;
this.setState({articles,flag:true});
}) }
render() {
let data = flag === false?"Loading...":this.state.articles.map(article => <li><a href={`${article.url}`}>{article.title}</a></li>)
return (
<div>
<ul>
{data}
</ul>
</div>
) }
}
export default Test;
I'm using react-lifecycle-component in my react app, and incurred in this situation where I need the componentDidMount callback to load some data from the backend. To know what to load I need the props, and I can't find a way to retrieve them.
here's my container component:
import { connectWithLifecycle } from "react-lifecycle-component";
import inspect from "../../../libs/inspect";
import fetchItem from "../actions/itemActions";
import ItemDetails from "../components/ItemDetails";
const componentDidMount = () => {
return fetchItem(props.match.params.number);
};
// Which part of the Redux global state does our component want to receive as props?
const mapStateToProps = (state, props) => {
return {
item: state.item,
user_location: state.user_location
};
};
// const actions = Object.assign(locationActions, lifecycleMethods);
export default connectWithLifecycle(mapStateToProps, { componentDidMount })(
ItemDetails
);
Any clues?
thanks.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import fetchItem from '../actions/itemActions'
class Container extends Component {
state = {
items: []
}
componentDidMount() {
const { match } = this.props
fetchItem(match.params.number)
// if your fetchItem returns a promise
.then(response => this.setState({items: response.items}))
}
render() {
const { items } = this.state
return (
<div>
{ items.length === 0 ? <h2>Loading Items</h2> :
items.map((item, i) => (
<ul key={i}>item</ul>
))
}
</div>
)
}
const mapStateToProps = (state, props) => {
return {
item: state.item,
user_location: state.user_location
}
}
export default connect(mapStateToProps)(Container)
Though I don't see where you are using the props you take from your Redux store...