Reactjs props keep getting re-rendered - reactjs

I am pretty newbie to react and its lifecycles, I am trying to get a specific profile data that created a specific post. I am able to receive data from the profile props but it has a strange behavior.
So when I console.log(profile) in render method, profile stays null along with rendering post list, then I get the value from the props, then it goes back to null again and eventually I am able to see the profile that exists. But during that loading, spinner and data blinks based on profile props.
Is it a good a idea to initialize a state with profile props? Or how could remove this behavior?
class PostItem extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.getProfileByUserId(this.props.post.user);
}
render() {
const { post } = this.props;
let username = ''
const { profile,loading } = this.props.profile;
if (!profile ) {
username = (
<span className={styles['username-style']}> {post.name + " "}</span>)
} else {
username = (
<Link to={`/profile/${post.user}`} className={styles["profile-link"]}>
{post.name + " "}
</Link>)
}
let postItemListView = "";
if (loading) {
postItemListView = (
<Grid>
<Grid.Column>
<Segment raised>
<Placeholder>
<Placeholder.Header image>
<Placeholder.Line />
<Placeholder.Line />
</Placeholder.Header>
<Placeholder.Paragraph>
<Placeholder.Line length="medium" />
<Placeholder.Line length="short" />
</Placeholder.Paragraph>
</Placeholder>
</Segment>
</Grid.Column>
</Grid>
);
} else {
postItemListView = (
<Link to={`/post/${post._id}`}>
<div className={styles["link-wrapper"]}>
<Grid>
<Grid.Column width={16}>
<Grid.Row style={{ padding: "10px 0" }}>
<h4 className={styles["subject-style"]}>
{post.subject.charAt(0).toUpperCase() +
post.subject.slice(1)}
</h4>
</Grid.Row>
<Grid.Row style={{ padding: "10px 0" }}>
<div className={styles["body-style"]}>{post.text}</div>
</Grid.Row>
<Grid.Row>
<div className={styles["detailed-text__style"]}>
{username}
created {moment(post.createdAt).fromNow()}
</div>
</Grid.Row>
</Grid.Column>
</Grid>
</div>
</Link>
);
}
return <div>{postItemListView}</div>;
}
}
PostItem.propTypes = {
post: PropTypes.object.isRequired,
profile: PropTypes.object.isRequired,
getProfileByUserId: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
auth: state.auth,
profile: state.profile
});
export default connect(
mapStateToProps,
{ getProfileByUserId }
)(React.memo(PostItem));
EDIT:
This how API requests happen in redux:
export const getProfiles = () => dispatch => {
dispatch(setProfileLoading());
axios
.get(`${PROFILE_API_URL}/all`)
.then(res =>
dispatch({
type: GET_PROFILES,
payload: res.data
})
)
.catch(err =>
dispatch({
type: GET_PROFILES,
payload: null
})
);
}
And this is the reducer for profile:
import {
GET_PROFILE,
GET_PROFILES,
PROFILE_LOADING,
CLEAR_CURRENT_PROFILE
} from 'actions/types';
const initialState = {
profile: {},
profiles: [],
loading: false
};
export default function(state = initialState, action) {
switch (action.type) {
case PROFILE_LOADING:
return {
...state,
loading: true
};
case GET_PROFILE:
return {
...state,
profile: action.payload,
loading: false
};
case GET_PROFILES:
return {
...state,
profiles: action.payload,
loading: false
};
case CLEAR_CURRENT_PROFILE:
return {
...state,
profile: null
};
default:
return state;
}
}

ok I'm not entirely sure of the problem in your app but I think you should do something like this
class PostItem extends Component {
constructor(props) {
super(props);
this.state = {
profile: null;
loading: true;
}
}
async componentDidMount() {
// make api request here and make it async
await const result = this.props.getProfileByUserId(this.props.post.user);
this.setState({profile: result, loading: false})
}
render() loading? (
<div> Loading... </div>
) : (
<div>
<Profile />
</div>
)
}
slight pseudo code but let me know if that helps?

Related

state and props are undefined

Hi Im fairly new to coding. I am using react redux and created a store, reducers etc. I cant get anything to render to the screen and when I console.log this.props it comes up an empty array. The data Im dealing with is an array of objects I set the initialstate to an array of objects. I connected all the components with connect and mapstateto props function, there is an empty object in the first arguement of the connect and the second arguement is the componenet. When I look at my component tree it looks fine but my state is undefined and cant figure out why? Here is my code. Thank you in advance.
export const FETCH_SMURFS_START ="FETCH__SMURFS_START"
export const FETCH_SMURFS_SUCCESS = "FETCH_SMURFS_SUCCESS"
export const FETCH_SMURFS_FAILURE ="FETCH_SMURFS_FAILURE"
export const ADD_SMURF_START = "ADD_SMURF_START"
export const ADD_SMURF_SUCCESS = "ADD_SMURF_SUCCESS"
export const ADD_SMURF_FAILURE = "ADD_SMURF_FAILURE"
export const getSmurfData = () =>dispatch=>{
dispatch({type:FETCH_SMURFS_START})
console.log(FETCH_SMURFS_START)
axios.get(' http://localhost:3333/smurfs')
.then((res)=>{
console.log(res.data)
dispatch({type:FETCH_SMURFS_SUCCESS, payload:res.datay})
})
.catch((err)=> {
dispatch({type:FETCH_SMURFS_FAILURE, payload:err.message})
})
}
export const putSmurfData = () =>dispatch=>{
dispatch({type:ADD_SMURF_START})
console.log(ADD_SMURF_START)
dispatch({ADD_SMURF_SUCCESS})
axios.put(' http://localhost:3333/smurfs')
.then((res)=>{
dispatch({type:ADD_SMURF_SUCCESS, payload:res.data})
})
.catch((err)=> {
dispatch({type:ADD_SMURF_FAILURE, payload:err.message})
})
}
class SmurfDisplay extends React.Component {
componentDidMount() {
getSmurfData();
}
render() {
return (
<>
<div>
{this.props.newSmurfData.map((smurf, index) => (
<div>
<h4 key={smurf.id}></h4>
<p> {index}</p>
<p>{smurf.description}</p>
<p>{smurf.nickname}</p>
<p>{smurf.name}</p>
<p>{smurf.position}</p>
</div>
))}
</div>
</>
);
}
}
const mapStateToProps = (state) => {
return {
newSmurfData: [
{
error:state.error,
id: state.id,
name: state.name,
position: state.position,
nickname: state.nickname,
description: state.description,
},
],
};
};
export default connect(mapStateToProps, {})(SmurfDisplay);
class Smurf extends React.Component {
render() {
console.log(this.props);
return (
<>
{this.props.smurfData.map(function(smurf,index) {
return(
<div>
<h4 key={index}></h4>
<p>{smurf.description}</p>
<p>{smurf.nickname}</p>
<p>{smurf.name}</p>
<p>{smurf.position}</p>
</div>
)
})}
</>
)
}
}
const mapStateToProps = (state) =>{
return{
smurfData:[{
error:state.error,
id:state.id,
name:state.name,
position:state.position,
nickname:state.nickname,
description:state.description
}]
}
}
export default connect(mapStateToProps,{})(Smurf)
xport const initialState = {
error:"",
isLoading: false,
smurfData : [{
id:"",
name:"",
position:"",
nickname:"",
description:""
}],
error:"",
isAddingSmurf:false,
newSmurfData:[{
id:"",
name:"",
position:"",
nickname:"",
description:""
}],
}
export const reducer = (state = initialState,action) =>{
switch(action.type){
case FETCH_SMURFS_START:
return{
...state,
isLoading:true,
error:""
}
case FETCH_SMURFS_SUCCESS:
return{
...state,
isLoading:false,
smurfData:action.payload
}
case FETCH_SMURFS_FAILURE:
return{
...state,
isLoading:false,
error:"there was an error getting your smurfs"
}
case ADD_SMURF_START:
return{
...state,
isAddingSmurf:false,
error:""
}
case ADD_SMURF_SUCCESS:
return{
...state,
isAddingSmurf:true,
error: "",
newSmurfData:action.payload
}
case ADD_SMURF_FAILURE:
return{
...state,
isAddingSmurf:false,
addingError:"Error"
}
default:
return state
}
}
export default reducer;
when you want to use this.props in your class Component you need to use super(props) inside the constructor:
constructor(props){
super(props)
}

Component Re-renders after clicking NavLink & loads same data Multiple times

I have made a sample e-commerce site for practice & I used React, Redux & Router-DOM. For the first time, everything loads perfectly. it has two routes ScreenShot-Link. Home route takes me to the Home page where the problem occurs & the Upload route takes me to the upload page. But when I again go back to HomeRoute the data which was fetched from an API doesn't re-render instead the state gets doubled I mean the existing state remains unchanged but for some reason, the Data gets fetched again and the elements get rendered for 2 times ScreenShot-2-Link.
Body Element which gets doubled if I route back to it from another route...
const Body = ({getProducts, deleteProduct, productState})=>{
const {products, loading, error} = productState;
useEffect(()=>{
getProducts()
}, [getProducts])
const deleteProducts = (id)=>{
deleteProduct(id)
}
return (
<div className="display-flex width-full flex-wrap justify-content-center">
{!loading && !error && products?products.map(product=>(
product.data?product.data.map(d=>(
<BodyDiv
key={uuid.v4()}
imgSrc={d.imgSrc}
title={d.title}
details={d.details}
description={d.description}
onClick={()=>deleteProducts(d._id)}
/>
)):product.map(d=>(
<BodyDiv
key={uuid.v4()}
imgSrc={d.imgSrc}
title={d.title}
details={d.details}
description={d.description}
onClick={()=>deleteProducts(d._id)}
/>
))
))
: loading&&!error
? <div className="vertical-center-strict horizontal-center-strict">Loading.......</div>
: <div className="vertical-center-strict horizontal-center-strict">No Internet!</div>
}
</div>
)
}
const BodyDiv = (props)=>{
return(
<div className="container-div width-full card tiny-padding align-self-start">
<img title={props.title} className="img-responsive hover-filter" src={props.imgSrc}/>
<h2 className="text-align-center">{props.title}</h2>
<h5>{props.details}</h5>
<p className="text-align-justify">{props.description}</p>
<button
className="btn btn-danger"
onClick={props.onClick}
>Delete</button>
</div>
)
}
BodyDiv.propTypes = {
title: PropTypes.string.isRequired,
img: PropTypes.string,
details: PropTypes.string.isRequired,
description: PropTypes.string,
onClick: PropTypes.func
}
const mapStateToProps = (state)=>({
productState: state.productState
})
const conn = connect(mapStateToProps, {getProducts, deleteProduct})(Body)
export default conn;
Router Component & Nav Component
//Parent Router Comonent
const App = () => {
return (
<Router>
<AppNavBar />
<Switch>
<Route path="/" exact>
<Body />
</Route>
<Route path="/upload">
<Upload />
</Route>
<Route path="*">
<div className="vertical-center-strict top-20">
<h1 className="text-align-center">404</h1>
<h3>Page doesn't exist</h3>
<p>Please give the correct address!</p>
</div>
</Route>
</Switch>
</Router>
);
};
export default App;
//NavLink Components..
const NavBar = ()=>{
return(
<>
<NavLink activeClassName="link" to="/" exact>
<NavItem content="Home" /> //<a> element with content as a prop
<NavLink>
<NavLink activeClassName="link" to="/upload">
<NavItem content="Upload"
</NavLink>
</>
)
}
}
My Redux Code: Store & Reducers
//Store
import {createStore, applyMiddleware} from "redux"
import rootReducer from "./reducers/index"
import thunk from "redux-thunk"
const middleWare = [thunk]
const store = createStore(rootReducer, applyMiddleware(...middleWare))
export default store
//RootReducer
import {
FETCHED_PRODUCTS,
FETCHING_PRODUCTS,
ERROR_GET_PRODUCTS,
DELETED_PRODUCT,
ERROR_DELETING_PRODUCT
} from "../actions/productActions";
const initialState = {
products: [],
loading: true,
error: false,
};
const productReducer = (state = initialState, action) => {
switch (action.type) {
case FETCHING_PRODUCTS:
return {
...state,
loading: true,
};
case FETCHED_PRODUCTS:
return {
...state,
products: state.products.concat(action.payload),
loading: false,
error: false,
};
case ERROR_GET_PRODUCTS:
return {
...state,
loading: false,
};
case DELETED_PRODUCT:
return {
...state,
products: state.products.map((product) =>
product.data.filter((d) => d._id !== action.payload)
),
error: false,
};
case ERROR_DELETING_PRODUCT:
return {
...state,
error: true,
};
default:
return state;
}
};
export default productReducer;
ActionCreators
export const getProducts = (payload)=>(dispatch)=>{
return(
fetch("/api/products")
.then(res=>{
dispatch({
type:FETCHING_PRODUCTS
})
if(!res.ok){
dispatch({
type: ERROR_GET_PRODUCTS
})
}
return res.json();
})
.then(json=>{
if(json)dispatch({
type: FETCHED_PRODUCTS,
payload: json
})
})
.catch(err=>{
console.log("Error!! failed to fetch data: "+ err)
})
)
}
export const deleteProduct = (payload)=>dispatch=>{
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
var requestOptions = {
method: 'DELETE',
headers: myHeaders,
body: JSON.stringify({"_id": payload}),
redirect: 'follow'
};
fetch("/api/product-delete", requestOptions).then(res=>{
dispatch({
type: DELETED_PRODUCT,
payload
})
if(res.status === 501 || res.status === 403){
dispatch({
type: ERROR_DELETING_PRODUCT,
})
}
})
.catch(err=>{
console.log("Failed to Delete")
})
}
Please help me I have been searching for the solution for 4 hours...It would be great if you help me...Thanks in advance
When using the useEffect hook the second parameter are dependencies.. so it calls the function again if the dependencies change. Now since getProductsis a function, it will never get called again. Instead, you should put products in there
useEffect(()=>{
getProducts()
}, [products])
//will refetch data everytime product state is updated.
It is a little bit hard to see, what is going on in the code. But I suspect maybe this is the problem. Otherwise let me know.

React context: send input data to another component

I have 3 components:
Search.js, Customers.js and Customer.js
In Search.js I have an input field. I want to send whatever value entered in the field over to the Customer.js component. I thought this would be straightforward, but I was wrong ...
I have also a context.js component that stores state for the application (I don't want to use redux because I don't know it yet).
Sorry but this is gonna be a long post as I want to give the background for this specific situation:
context.js
const Context = React.createContext();
const reducer = (state, action) => {
switch (action.type) {
case "SEARCH_CUSTOMERS":
return {
...state,
customer_list: action.payload,
firstName: ''
};
default:
return state;
}
};
export class Provider extends Component {
state = {
customer_list: [],
firstName: "",
dispatch: action => this.setState(state => reducer(state, action))
};
componentDidMount() {
axios
.get("/api")
.then(res => {
console.log(res.data);
this.setState({ customer_list: res.data });
})
.catch(error => console.log(error));
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
);
}
}
export const Consumer = Context.Consumer;
Search.js: the input value I want to send to Customer is 'firstName'
class Search extends Component {
state = {
firstName: ""
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
findCustomer = (dispatch, e) => {
e.preventDefault();
axios
.get("/api/customers", {
params: {
firstName: this.state.firstName,
}
})
.then(res => {
dispatch({
type: "SEARCH_CUSTOMERS",
payload: res.data
});
this.setState({ firstName: "" });
});
};
return (
<Consumer>
{value => {
const { dispatch } = value;
return (
<form onSubmit={this.findCustomer.bind(this, dispatch)}>
<div className="form-group">
<input
ref={input => {
this.nameInput = input;
}}
type="text"
name="firstName"
value={this.state.firstName}
onChange={this.onChange}
/>
the Customers.js:
class Customers extends Component {
render() {
const key = Date.now();
return (
<Consumer>
{value => {
const { customer_list} = value;
if (customer_list === undefined || customer_list.length === 0) {
return <Spinner />;
} else {
return (
<React.Fragment>
<h3 className="text-center mb-4">{heading}</h3>
<div className="row">
{customer_list.map(item => (
<Customer key={item.key} customer={item} />
))}
</div>
</React.Fragment>
);
}
}}
</Consumer>
);
}
}
export default Customers;
and Finally theCustomer.js: this is where I want the input value to be displayed:
const Customer = props => {
const { customer } = props;
return (
<div className="col-md-12">
<div className="card-body">
<strong>{customer.firstName}</strong> // not working
...
}
the {customer.firstName} does not show the value.
Is is necessary to go through the intermediate Customers.js component to pass the input value?
I would like to keep the architecture as is (with the context.js) and display the value in the Customer.js component.

React pagination on scroll

i have to implement chat system in react i m doing it first time and i m stuck.i have to pass page no to backend api to get new data every time.and i have to pass page no to api on scroll. i m using
[1]: https://www.npmjs.com/package/react-infinite-scroller
i m getting total data count and 9 data array per page from api.scroll upto which total count is available and when user scroll to top is should load more.i have tried lots of module but failed to implement pagination on scroll.using react-infinite-scroll module i m getting page no but its not working as i want.Please suggest me right way of doing it
here is my component code
const mapStateToProps = state => ({
users: state.sidebarUser.users,
total:state.sidebarUser.total,
routing: state.routing,
converSationId: state.getConversationId.data
});
const mapDispatchToProps = dispatch => ({
loadUserList: (page={}) => (dispatch(getSideBarUser(page))),
getConversationId: (userId) =>
dispatch(getConversationId(userId)),
loadUserContent: id => dispatch(UserChatList(id))
});
class SidebarContainer extends Component {
constructor(props) {
super(props);
this.state={
isLoading:false,
sidebar:[],
page:0,
hasMore: true,
}
this.getPosts=this.getPosts.bind(this);
}
componentDidMount() {
const {
location: { search }
} = this.props.routing;
let userId = new URLSearchParams(search).get("id");
this.props.loadUserList({page:1});
this.setState({page:this.state.page+1});
this.props.getConversationId(userId);
}
getPosts(page) {
console.log("pgae---->",page)
console.log("this.props--->",this.props.users)
this.props.loadUserList({page:page});
}
render() {
const { users } = this.props;
const {hasMore,sidebar} =this.state;
return (
<div className="chatting-user-list-section" ref={(ref) => this.scrollParentRef = ref} >
<InfiniteScroll
initialLoad={false}
pageStart={0}
loadMore={this.getPosts.bind(this)}
hasMore={hasMore}
getScrollParent={() => this.scrollParentRef}
threshold={520}
loader={<div className="loader">Loading ...</div>}>
<SidebarComponent users={users} listClicked={this.listClicked} />
</InfiniteScroll>
</div>)
}
}
export const Sidebar = connect(
mapStateToProps,
mapDispatchToProps
)(SidebarContainer)
and here is my Reducer
import { SIDEBAR_USERS_SUCCESS, SIDEBAR_USERS_FAILURE } from './ActionTypes';
const initialState = {
users: [],
total: 0
}
export const sidebarUser = (state = initialState, { type, payload }) => {
switch (type) {
case SIDEBAR_USERS_SUCCESS: {
return { ...state, ...payload };
}
case SIDEBAR_USERS_FAILURE: {
return { ...state, error: payload }
}
default:
return state;
}
};

React-Redux: Cannot read property 'map' of undefined when deleting an item

I have an error after clicking the delete button saying:
Cannot read property 'map' of undefined.
I'm new in React Redux JS.
Please see my code below of my component reducers and actions:
Post.js
class Post extends Component {
constructor(){
super();
this.deletePost = this.deletePost.bind(this);
}
deletePost(postId){
this.props.deletePost(postId);
}
render(){
const postItems = this.props.posts.map(post => (
<div key={post.id} className="row">
<div className="container">
<h3>{post.title}</h3>
<p>{post.body}</p>
<button
onClick={() =>this.deletePost(post.id)}
className="btn btn-danger">
Delete
</button>
</div>
</div>
))
const divStyle = {
padding: '15px',
}
return (
<div style={divStyle}>
<PostForm />
<hr/>
{postItems}
</div>
)
}
}
const mapStateToProps = state => ({
posts: state.posts.items,
newPost: state.posts.item
})
export default connect(mapStateToProps, { fetchPosts, deletePost })(Post);
PostAction.js (Here is my delete action. I am using jsonplaceholder API post.)
export const deletePost = (postId) => dispatch => {
fetch('https://jsonplaceholder.typicode.com/posts/'+postId, {
method: 'DELETE',
})
.then(dispatch({
type: DELETE_POST,
payload: postId
}));
}
PostReducer.js (This is my reducer.)
case DELETE_POST:{
const newState = Object.assign([], state);`enter code here`
const filteredItems = newState.items.filter(items => {
return items.id != action.payload;
});
return filteredItems;
}
case DELETE_POST:{
const { items } = state;
const filteredItems = items.filter(items => {
return items.id != action.payload;
});
return {
...state,
items: [ ...filteredItems ]
};
}
Yes just replace
return filteredItems; to return { items: filteredItems }
But please can you check my code if it's correct. Thanks

Resources