I have problem with calling componentDidMount.
Situation:
I have 2 reducers. They called in different components and do not intersect. In every component in ComponentDidMount i calling function from Reducer which set data. In different ways one of two is not working and function from reducer is not called. I have error like "Cannot read property of null". After reload page working of components is changed.
First Reducer:
const SET_VIDEOS = 'SET_VIDEOS';
const TOOGLE_IS_FETCHING = 'TOOGLE_IS_FETCHING';
let initialState = {
videos: null,
isFetching: null
}
const youtubeReducer = (state = initialState, action) => {
switch(action.type){
case SET_VIDEOS: {
console.log("muerto.");
return {...state, videos: action.videos}
}
case TOOGLE_IS_FETCHING: {
return {...state, isFetching: action.isFetching}
}
default:{
return state;
}
}
}
export const setVideos = (videos) => ({
type: SET_VIDEOS, videos
});
export const setIsFetching = (isFetching) => ({
type: TOOGLE_IS_FETCHING, isFetching
});
export const getVideosThunkCreator = () => {
return(dispatch) => {
dispatch(setIsFetching(true));
console.log("ALO");
youtubeApi.getVideos().then(data => {
dispatch(setVideos(data.items));
dispatch(setIsFetching(false));
console.log(data.items);
})
}
}
Second Reducer:
let initialState = {
dj: null,
isFetching: null
}
const djReducer = (state = initialState, action) => {
switch(action.type){
case SET_DJ:{
console.log("muerto2.");
return {...state, dj: action.dj}
}
case TOOGLE_IS_FETCHING: {
return {...state, isFetching: action.isFetching}
}
default:
return state;
}
}
export const setDj = (dj) => ({
type: SET_DJ, dj
});
export const setIsFetching = (isFetching) => ({
type: TOOGLE_IS_FETCHING, isFetching
});
Container Component with First Reducer:
class LivesContainer extends React.Component{
componentDidMount(){
this.props.getVideosThunkCreator();
console.log(this.props);
}
render(){
return(
<>
{this.props.isFetching ? <Preloader/> : <Lives videos={this.props.videos}/>}
</>
);
}
}
let mapStateToProps = (state) => {
return{
videos: state.youtubeReducer.videos,
isFetching: state.youtubeReducer.isFetching
}
}
export default connect(mapStateToProps, {
getVideosThunkCreator,
setIsFetching
})(LivesContainer);
Container Component with Second Reducer:
class DjContainer extends React.Component {
componentDidMount(){
console.log("MOUNT");
let djId = this.props.match.params.djId;
if(!djId){
djId = 0;
}
let djs = this.props.djs;
this.props.setIsFetching(true);
console.log(djs);
console.log(djId);
djs.forEach(dj => {
if(dj.id == djId){
this.props.setDj(dj);
this.props.setIsFetching(false);
}
});
console.log(this.props);
}
componentWillUnmount(){
console.log("UNMAUNT");
}
render(){
return(
<>
{this.props.isFetching ? <Preloader/>:
<Dj {...this.props} dj={this.props.dj} />
}
</>
);
}
}
let mapStateToProps = (state) => ({
dj: state.djReducer.dj,
djs: state.djsReducer.djs,
isFetching: state.djReducer.isFetching
});
let WithUrlDataContainerComponent = withRouter(DjContainer);
export default connect(mapStateToProps,{
setDj,
setIsFetching
})(WithUrlDataContainerComponent);
First Component where i have errors:
const Lives = (props) => {
let bannerId;
let videoBanner = "https://www.youtube.com/embed/";
let videos = [];
props.videos.forEach((video,index) => {
if(index == 0){
bannerId = video.contentDetails.videoId;
}else{
videos.push(video);
}
});
console.log(videos);
videoBanner += bannerId;
let videosMap = videos.map((video,index) => {
return <Video video={video} key={index}/>
})
return(
<div className={classes.main}>
<div className={classes.container}>
<h1>Our videos</h1>
<div className={classes.videos}>
<iframe width="90%" height="80%" src={videoBanner} frameborder="0" allow="accelerometer; autoplay=1; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<div className={classes.videoList}>
{videosMap}
</div>
</div>
</div>
</div>
);
}
error from there:
props.videos.forEach((video,index) => {
if(index == 0){
bannerId = video.contentDetails.videoId;
}else{
videos.push(video);
}
});
From Secornd Component:
const Dj = (props) => {
console.log(props);
let video = "https://www.youtube.com/embed/" + props.dj.video;
return(
<div className={classes.main}>
<div className={classes.container}>
<h1>{props.dj.name}</h1>
<iframe width="90%" height="80%" src={video} frameborder="0" allow="accelerometer; autoplay=1; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<div className={classes.links}>
<a href={props.dj.fb} target="_blank">
<img src={fb}/>
</a>
<a href={props.dj.sound} target="_blank">
<img src={sound}/>
</a>
<a href={props.dj.inst} target="_blank">
<img src={inst}/>
</a>
</div>
</div>
</div>
);
}
Error from there:
let video = "https://www.youtube.com/embed/" + props.dj.video;
Related
I have this working stateful component in React:
import React, {Component} from "react";
class MyComponent extends Component {
constructor() {
super();
this.myRef = React.createRef();
this.state = {
array: [],
noresults: false
};
}
loadData = () => {
let data = this.myRef.current.value;
let url = "someurl="+data;
if(!data) {
return;
}
fetch(url)
.then((res) => res.json())
.then((json) => {
if(json.data.length > 0) {
this.setState({
array: json.data,
noresults: false
});
} else {
this.setState({
noresults: true
});
}
})
}
render() {
const { array, noresults } = this.state;
return (
<div>
<section>
<input ref={this.myRef} type="number"/>
<button onClick={this.loadData}>Click Here</button>
</section>
<ul>
{
array.map((e) => (
<li key = { e.key } >
{ e.data }
</li>
))
}
</ul>
{noresults && <div>No Records</div>}
</div>
);
}
}
export default MyComponent
I want to convert this to stateless like this:
function MyComponent() {
return (
<div>
<section>
<input type="number"/>
<button>Click Here</button>
</section>
<ul>
<li></li>
</ul>
<div>No Records</div>
</div>
);
}
export default MyComponent
Now how can I pass data of input to my method to make API call. Also how to pass the response from API to display as ul and li elements?
Just pass it as component props:
const MyComponent = ({array = [], loadData = () => {}}) => {
const [inputValue, setInputValue] = useState();
const handleInputChange = (evt) => {
setInputValue(evt.target.value);
};
return (
<div>
<section>
<input type="number" onChange={handleInputChange} />
<button onClick={e => loadData(e, inputValue)}>Click Here</button>
</section>
<ul>
{array.map((e) => (<li key={e.key}>{e.data}</li>))}
</ul>
{array.length === 0 && <div>No Records</div>}
</div>
);
}
For input, I created a local state which is updated on input change and passed it to loadData function. You can access the current value by parametrizing loadData function:
loadData = (e, currentInputValue) => { ... };
Hello I have an action to get data from my backend and then I created a new container and I use this state and I created an action to filter this state, and then when it clicks back to my component product the products that were filtered
My action fetch:
export const fetchProduct = () => {
console.log('action')
return dispatch => {
dispatch(fetchStarted());
api
.get('/products')
.then(res => {
dispatch(fetchSucess(res.data));
})
.catch(err => {
dispatch(fetchFailed(err.message));
});
};
};
My reducer fetch:
const initialState = {
loading: false,
products: [],
error: null
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case FETCH_LOADING:
return {
...state,
loading: true
};
case FETCH_SUCESS:
return {
...state,
loading: false,
error: null,
...state, products: action.data
};
case FETCH_FAIL:
return {
...state,
loading: false,
error: action.error
};
default:
return state;
}
}
My container product:
class HomeProducts extends Component {
componentDidMount() {
this.props.fetchProduct();
}
render() {
const productItems = this.props.products.map( product =>(
<div className="col-md-4 pt-4 pl-2" key={product.id}>
<div className = "thumbnail text-center">
<a href={`#${product.id}`} onClick={(e)=>this.props.handleAddToCard(e,product)}>
<p>
{product.name}
</p>
</a>
</div>
<b>{util.formatCurrency(product.price)}</b>
<button className="btn btn-primary" onClick={(e)=>this.props.handleAddToCard(e,product)}>Add to Cart</button>
</div>
)
)
return (
<div className="container">
<div className="row">
{productItems}
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return{
products: state.data.products,
loading: state.data.loading,
error: state.data.error
}
};
const mapActionsToProps = {
fetchProduct: fetchProduct
};
export default connect(mapStateToProps, mapActionsToProps)(HomeProducts);
My product container will show in a div all the products it fetched in the backend.
And now My container filter:
class FilterHome extends Component {
render() {
console.log(this.props);
return (
<>
<div className="row">
<button className="filterbt btn btn-danger btn-rounded">Filters</button>
<div className=" mt-4 d-flex flex-column">
<p className="textCategory">CATEGORY</p>
<div className="category d-flex flex-column">
<p>Stat Trak</p>
<p>Normal</p>
</div>
<p className="textCategory">EXTERIOR</p>
<div className="category d-flex flex-column">
<p value={2} onChange={(e) => this.props.filterProducts(this.props.products, e.target.value)}>Factory New ()</p>
<p>Minimal Wear ()</p>
<p>Field Tested ()</p>
<p>Well Worn ()</p>
<p>Battle Scarred ()</p>
</div>
</div>
</div>
</>
)
}
}
const mapStateToProps = state => ({
products: state.data.products,
});
export default connect(mapStateToProps, {filterProducts})(FilterHome);
and then I created an action and a redux to get the filtered product list and then send this filtered stripe to my div that displays the products.
Action filter products:
import {FILTER_PRODUCT} from '../constants/fetchTypes';
export const filterProducts = (products,value) => (dispatch) => {
return dispatch({
type:FILTER_PRODUCT,
payload:{
value:value,
products:value === ''? products: products.filter(v => v.id_sub = value)
}
})
}
My reducer filter:
import {
FETCH_SUCESS,
FETCH_FAIL,
FETCH_LOADING,
FILTER_PRODUCT
} from '../constants/fetchTypes';
const initialState = {
products: []
};
export default function productReducer(state = initialState, action) {
switch (action.type) {
case FILTER_PRODUCT:
return {
...state,
filteredProducts: action.payload.products
};
default:
return state;
}
} return state;
}
}
I don't know how I can change the filtered products in my product div
I am stuck around a project and honestly I don't know how to solve it (I am quite new before you judge)
so this is my code:
class EditProfile extends Component {
state = {
företagsnamn: '',
organisationsnummer: '',
};
handleChange = e => {
this.setState({
[e.target.id]: e.target.value
});
};
handleSubmit = e => {
e.preventDefault();
// console.log(this.state);
this.props.editProfile(this.state);
this.props.history.push("/dash");
};
render() {
const { auth, profile } = this.props;
if (auth.isEmpty) return <Redirect to="/dash" />;
return (
<div >
<form className="white" onSubmit={this.handleSubmit}>
<div className="row">
<div className="col xl6 l6 m6 s12">
<label>Foretagsnamn:</label>
<input
type="text"
disabled
placeholder={profile.foretagsnamn}
id="foretagsnamn"
onChange={this.handleChange}
/>
</div>
<div className="col xl6 l6 m6 s12">
<label>organisationsnummer:</label>
<input
type="number"
placeholder={profile.organisationsnummer}
id="organisationsnummer"
onChange={this.onChange}
/>
</div>
</div>
<div className="input-field">
<button className="btn orange lighten-1" style={{width:'100%'}} >Submit</button>
{ }
</div>
</form>
</div>
}}
const mapStateToProps = state => {
return {
auth: state.firebase.auth,
profile: state.firebase.profile
};
};
const mapDispatchToProps = dispatch => {
return {
editProfile: profil => dispatch(editProfile(profil))
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(EditProfile);
this was the action
export const editProfile = (profil) => {
return (dispatch, getState, { getFirestore, getFirebase }) => {
const firebase = getFirebase();
const firestore = getFirestore();
const profile = getState().firebase.auth
console.log(profile)
const authorId = getState().firebase.auth.uid;
// const foretagsnamn = getFirestore().firestore.collection('users').doc(profile.uid).foretagsnamn
// firebase.auth()
firestore.collection('users').doc(profile.uid).set({
// foretagsnamn: foretagsnamn,
// organisationsnummer: profil.organisationsnummer,
adress: profil.adress,
ort: profil.ort,
telefonnummer: profil.telefonnummer,
postnummer: profil.postnummer,
}, { merge: true }
).then(() => {
dispatch({ type: 'UPDATE_SUCCESS' });
}).catch(err => {
dispatch({ type: 'UPDATE_ERROR' }, err);
});
}}
and this the reducer
const editProfileReducer = (state = initState, action) => {
switch (action.type) {
case "EDITPROFILE_ERROR":
return {
...state,
editError: action.error
};
case "EDITPROFILE_SUCCESS":
return {
...state,
user:action.user
};
default:
return state;
}
}
export default editProfileReducer;
however when I press the button submit it shows this error:
FirebaseError: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: undefined
PS: Solved. The action was wrong. I changed ´´´const profile = getState().firebase.auth.```**instead of profile. **
Stays open if someone needs.
I'm having a problem with redux.
when I use
store.dispatch( addExpense( {description: "Rent"} ) );
in my app.js file it works and the object is added. But when I try to use it in a context of an component in a separated file it doesn't. The console does't throw any error.
When I use other action like "search" in the same component it works fine. So there's no problem with the connection. It seems that for some reasons it can't change the state.
//ACTIONS
export const addExpense = ( { description="", amount=0 } = {}) => ({
type: "ADD_EXPENSE",
expense: {
description,
amount
}
})
//REDUCERS
const expenseReducer = ( state = [], action) => {
switch(action.type) {
case "ADD_EXPENSE":
return [...state, action.expense]
case "EDIT_EXPENSE": //<- this works
return state.map( (expense) => {
if (expense.id === action.id)
return {
...expense, ...action.update }
else return expense
} )
default: return state
}
const filterReducer = ( state = {text:""}, action) => {
switch(action.type){
case "FIND_TEXT": //<- this works
return { ...state, text:action.text }
default: return state;
}
}
//COMPONENT
const AddEx = ( props ) => (
<div>
<form onSubmit={(e) => {
e.preventDefault;
props.dispatch(addExpense ( {description: document.getElementById("addedEx").value} ))
console.log(document.getElementById("addedEx").value);
//it shows the correct value in the console but the state stays unchanged
} } >
<input type="text" id="addedEx"/>
<button type="submit">submit</button>
</form>
//SEARACH -> works
<input
type="text" value={props.filter.text}
onChange={(e) => {
props.dispatch(findText({text:e.target.value}))
}}
/>
</div>
)
You are adding the value to state instead of expenses, do return [...state.expense, action.expense]
//REDUCERS
const expenseReducer = (state = [], action) => {
switch (action.type) {
case 'ADD_EXPENSE':
return {
...state,
expense: [...state.expense, action.expense]
};
case 'EDIT_EXPENSE': //<- this works
return state.map(expense => {
if (expense.id === action.id)
return {
...expense,
...action.update
};
else return expense;
});
default:
return state;
}
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.1/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.0.3/react-redux.min.js"></script>
<script src="http://wzrd.in/standalone/uuid%2Fv1#latest"></script>
<div id="root"></div>
<script type="text/babel">
const { Provider, connect } = ReactRedux;
const { applyMiddleware, createStore, combineReducers } = Redux;
const ADD_EXPENSE = 'ADD_EXPENSE';
function addExpense(payload) {
return { type: ADD_EXPENSE, payload };
}
const initialState = {
expense: [],
};
function rootReducer(state = initialState, action) {
if (action.type === ADD_EXPENSE) {
return {
...state,
expense: [...state.expense, action.payload]
};
}
return state;
}
const store = createStore(rootReducer);
const mapStateToProps = state => {
return { expense: state.expense };
};
function mapDispatchToProps(dispatch) {
return {
addExpense: expense => dispatch(addExpense(expense))
};
}
const ConnectedList = ({ expense, addExpense }) => {
return (
<div>
<ul className="list-group list-group-flush">
{expense.map(el => (
<li className="list-group-item" key={el.id}>
{`${el.description} - $${el.amount}`}
</li>
))}
</ul>
</div>
);
};
const List = connect(
mapStateToProps,
)(ConnectedList);
class ExpenseForm extends React.Component {
state = {
description: '',
amount: 0,
}
handleSubmit = (e) => {
e.preventDefault();
if (!this.state.description || this.state.amount === 0) {
return
}
const { description, amount } = this.state;
this.props.addExpense({
description,
amount,
id: uuidv1()
});
this.setState({
description: '',
amount: 0
})
}
handleInput = (e) => {
this.setState({
[e.target.name]: e.target.value
})
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<input name="description" placeholder="Description" onChange={this.handleInput} value={this.state.description} />
<input type="number" name="amount" placeholder="Amount" onChange={this.handleInput} value={this.state.amount} />
<input type="submit" />
</form>
)
}
}
const Form = connect(null, mapDispatchToProps)(ExpenseForm);
class App extends React.Component {
render() {
return (
<div>
<List />
<Form />
</div>
);
}
}
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
</script>
I found the solution. It didn't work because e.preventDefault lacked the (). so simple change from e.preventDefault to e.preventDefault () fixed it. silly mistake.
What is the proper way of converting the following codes to stateless component?
export default class About extends Component {
state = {
showKitten: false
}
handleToggleKitten = () => this.setState({ showKitten: !this.state.showKitten });
render() {
const { showKitten } = this.state;
const kitten = require('./kitten.jpg');
return (
<div className="container">
{showKitten && <div><img src={kitten} alt="kitchen" /></div>}
</div>
);
}
}
Managed to define the props, etc. The following code works on logging a message. But what would be the best way to toggle boolean?
const handleToggleKitten = () => {
console.log('Hello from here');
**// How to toggle the value of boolean here?**
};
const About = (props) => {
const { showKitten } = props;
const kitten = require('./kitten.jpg');
return (
<div className="container">
{showKitten && <div><img src={kitten} alt="kitchen" /></div>}
</div>
);
};
About.defaultProps = {
showKitten: false
};
About.propTypes = {
showKitten: PropTypes.bool.isRequired
};
You should be having a stateful component that renders the stateless component and passes and updates props to it
class App extends React.Component {
state= {showKitten: false}
handleToggleKitten = () => {
this.setState((prevState, props) => ({
showKitten: !prevState.showKitten
}))
};
render() {
return (
<About showKitten={this.state.showKitten} handleToggleKitten={this.handleToggleKitten}/>
)
}
}
const About = (props) => {
const { showKitten } = props;
return (
<div className="container">
{showKitten && <div><img src={"http://addolo.com/wp-content/uploads/2016/12/kitten-pics-uncategorized-84-astonishing-photo-ideas-kittens-cattime-black-and-white-pictures-funny-with-captionskitten-cutekitten.jpg"} alt="kitchen" /></div>}
<button onClick={props.handleToggleKitten}>Toggle</button>
</div>
);
};
About.propTypes = {
showKitten: React.PropTypes.bool.isRequired
};
ReactDOM.render(<App/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
Hey Yen Sheng a bit late but try this.
let showKitten = false;
const handleToggleKitten = () => {
console.log('Hello from here');
**// How to toggle the value of boolean here?**
showKitten = !showKitten;
};