Hi i just started learning react.
Is it possible to do this without classes (functional programming)?
Index.js has a button with an axios call.
When the answer came, a notification should appear and disappear in a second.
App.js
import React from 'react';
import {BrowserRouter, Route} from 'react-router-dom';
import Index from './components/index/index';
import Notifications from './components/notifications/notifications';
const App = (props) => {
return (
<BrowserRouter>
<Route exact path="/" render={ () => <Index notification={ <Notifications/> } /> } />
</BrowserRouter>
);
}
export default App;
Index.js
import React from 'react';
const axios = require('axios');
const Index = (props) => {
let getData = () => {
axios.get('url')
.then(function (response) {
<Notification text={ response.data }/> );
})
.catch(function (error) {
console.log(error);
});
}
return (
<button onClick={ () => getData() }>Get data</button>
);
}
export default Index;
Notification.js
import React from 'react';
const Notification = (props) => {
return (
<div>
<div>
<p>props.text</p>
</div>
</div>
);
//and delete after 1 second
}
export default Notification;
Please show examples of functional solutions :)
In your axios.then, you can store the result in state, and set a timeout to clear the state 1s later. Then you render Notification if there is something in state
const Index = (props) => {
const [text, setText] = useState();
let getData = () => {
axios.get('url')
.then(function (response) {
setText(response.data);
setTimeout(() => setText(), 1000);
})
.catch(function (error) {
console.log(error);
});
}
return (
<>
<button onClick={() => getData()}>Get data</button>
{text &&
<Notification text={text} />
}
</>
);
}
To render a notification in the screen, normally I would use React Portals
In order to do that your Notification component need to render to DOM through Portal
const notificationRoot = document.getElementById('notification-root'); // Create the element in your main html file in order for your notification to "portal" in
const Notification = (props) => {
return (
<div>
<div>
<p>props.text</p>
</div>
</div>
);
};
const DisplayNotification = () => {
const domNode = usememo(() => document.createElement('div'), []);
useEffect(() => {
notificationRoot.appendChild(domNode);
return () => {
notificationRoot.removeChild(domNode);
}
}, [])
return ReactDOM.createPortal(
<Notification />,
domNode
); // Portal to your node
}
By rendering DisplayNotification, your Notification should pop up.
You should use redux for achieve this, when you receive data from API, dispatch a redux action who return a true/false boolean.
The benefit of this proposal solution, after you developing system, you need to call only one function, and dispatch this into your store that's it !!
Place you <Notification /> component at top of your app
Like :
const App = (props) => {
return (
<Provider store={store}>
<Notification />
<BrowserRouter>
<Route exact path="/" render={/* YOUR HOMEPAGE COMPONENT */} />
</BrowserRouter>
</Provider>
);
}
Please look redux solution here : https://redux.js.org/introduction/getting-started
Inside your <Notification />
Don't forget to connect at redux you should use the connect() is an HOC (High Order Component)
import React from 'react';
import { connect } from 'redux'
const Notification = (props) => {
return (
<div>
<div>
<p>props.text</p>
</div>
</div>
);
//and delete after 1 second
}
const mapStateToProps = (state) => {
/* Get your state from your store notification for example */
return {}
}
export default connect(mapStateToProps)(Notification);
Related
**I'm getting react Error while trying to change parent component styles onMouseEnter event. How could I change the styles via child component button?
The error is - Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
That seems odd for onMouseLeave={() => setHovered(false)} is an arrow function.
https://codesandbox.io/s/trusting-moon-djocul?
**
// App js
import React, { useState, useEffect } from "react";
import Categories from "./Categories";
import ShopPage from "./components/Products";
export default function App() {
const [data, setData] = useState(Categories);
useEffect(() => {
setData(data);
}, []);
return (
<div className="wrapper">
<ShopPage products={data} filterResult={filterResult} />
</div>
);
}
// Shopping page
const ShopPage = ({ products }) => {
return (
<>
<div>
<Products products={products} />
</div>
</>
);
};
// Parent component, the main part goes here
const Products = ({products}) => {
const [hovered, setHovered] = useState(false);
const [style, setStyle] = useState(false);
if (hovered) {
setStyle({
// inline styles
});
} else {
setStyle({
// inline styles
});
}
return (
<>
<Product setHovered={setHovered} style={style} products={products}/>
</>
);
};
export default Products;
// Child component
const Product = ({ setHovered, style, products }) => {
return (
<div className={styles.items}>
{products.map((value) => {
return (
<>
<div style={style}>
<button
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
Add to Cart
</button>
</div>
</>
);
})}
</div>
);
};
export default Product;
The issue is you are setting setHovered state in component, the simple solution could be to use it in useEffect and add required dependency.
If we talk about your code so you can easily do this by using the state in child component instead of passing through props.
I have updated your code below:
https://codesandbox.io/s/nameless-cookies-e6pwxn?file=/src/components/Product.js:141-151
I'm trying to show data from an API call. The structure of the application looks like
MainComponent -> RefreshButton (this will fetch the data)
MainComponent -> ShowData (this will show the data that is being fetched)
MainComponent has a state userData that will store the response that was received from the API. Now the issue is, whenever I'm clicking the button, it is getting into an infinite loop of rendering and calls the API infinite times.
This is what the error shows:
Here is my MainComponent -
import React, { useEffect, useState } from "react";
import RefreshButton from "./RefreshButton";
import ShowData from "./ShowData";
const MainComponent = () => {
const [userData, setUserData] = useState();
useEffect(() => {
console.log(userData);
}, [userData]);
return (
<div>
<p style={{ textAlign: "center" }}>Main Component</p>
<RefreshButton setUserData={setUserData} />
{userData && <ShowData userData={userData} />}
</div>
);
};
export default MainComponent;
Here is my RefreshButton component -
import React from "react";
import axios from "axios";
const RefreshButton = ({ setUserData }) => {
const getData = () => {
axios
.get(`https://jsonplaceholder.typicode.com/todos`)
.then((response) => {
if (response.status === 200) setUserData(response.data);
})
.catch((err) => {
console.log(err);
});
};
return (
<div className="button-container">
<button className="fetch-data-button" onClick={() => getData()}>
Fetch new data
</button>
</div>
);
};
export default RefreshButton;
And here is my ShowData component -
import React from "react";
const ShowData = ({ userData }) => {
console.log("Here", userData);
return (
<>
{userData.map((info, idx) => (
<div className="user-data" key={idx}>
{info}
</div>
))}
</>
);
};
export default ShowData;
PS - I'm new to React and couldn't find a potential solution on this, there are several tutorials on how to fetch data from API calls and show it, but I wanted to know what I'm doing wrong here. Thanks in advance!
You might have misunderstood with the infinite loop error
It's actually a render error as being shown here:
To fix your render error, simply put an actual string variable in the {}
Because the response was an array of this object, so you can't simply render the whole object but need to pick an actual string variable inside:
[{
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
}],
Change to something like this:
const ShowData = ({ userData }) => {
console.log("Here", userData);
return (
<>
{userData.map((info, idx) => (
<div className="user-data" key={idx}>
{info.title} // <-- Put a title here.
</div>
))}
</>
);
};
Remove
useEffect(() => {
console.log(userData);
},[userData])
This will reevaluate component whenever user data changes, which Leeds to call showData infinitely
I need to render a component that has a route using react router. the first component has a button that when clicked needs to render another component that has state passed in from the first component. All objects and strings from the first component show in the console.log of the child component but it wont set state when I use setProfile(p).
const Member = (props)=> {
const [user, setUser] = useState({});
const [profile, setProfile] = useState({});
// run effect when user state updates
useEffect(() => {
const doEffects = async () => {
try {
const pro = socialNetworkContract.members[0]
console.log(pro)
const p = await incidentsInstance.usersProfile(pro, { from: accounts[0] });
const a = await snInstance.getUsersPosts(pro, { from: accounts[0] });
console.log(a)
console.log(p)
setProfile(p)
} catch (e) {
console.error(e)
}
}
doEffects();
}, [profile, state]);
const socialNetworkContract = useSelector((state) => state.socialNetworkContract)
return (
<div class="container">
<a target="_blank">Name : {profile.name}</a>
{socialNetworkContract.posts.map((p, index) => {
return <tr key={index}>
{p.message}
</tr>})}
</div>
)
}
export default Member;
This is the parent component I want to redirect from
const getProfile = async (member) => {
const addr = dispatch({ type: 'ADD_MEMBER', response: member })
console.log(member)
}
const socialNetworkContract = useSelector((state) => state.socialNetworkContract)
return (
<div>
{socialNetworkContract.posts.map((p, index) => {
return <tr key={index}>
<button onClick={() => getProfile(p.publisher)}>Profile</button>
</tr>})}
</div>
)
}
export default withRouter(Posts);
I have this component working when I don't have a dynamic route that needs data passing in from the parent component It's redirecting from.
This is my routes.js file
const Routes = () => {
return (
<Switch>
<Route path="/posts" exact component={Posts} />
<Route path="/member" exact component={Member} />
<Redirect exact to="/" />
</Switch>
)
}
export default Routes
https://codesandbox.io/s/loving-pine-tuxxb
Given the following two components, I expect the EntryList component to re-render after the state changes in the handleEnttryDelete after the button in EntryForm is clicked. Currently the state changes, but the UI isn't updating itself:
import React, { useState } from "react";
import Button from "#material-ui/core/Button";
import { render } from "#testing-library/react";
const EntryList = (props) => {
const [entryList, setEntryList] = useState(props.data);
const handleEntryDelete = (entry) => {
const newState = entryList.filter(function (el) {
return el._id != entry._id;
});
setEntryList(() => newState);
};
return (
<div>
{entryList.map((entry) => {
return (
<EntryForm entry={entry} handleEntryDelete={handleEntryDelete} />
);
})}
</div>
);
};
const EntryForm = (props) => {
const [entry, setEntry] = useState(props.entry);
return (
<div>
<Button onClick={() => props.handleEntryDelete(entry)}>
{entry._id}
</Button>
</div>
);
};
export default EntryList;
Your code probably works, but not as intended. You just have to use key while mapping arrays to components.
Therefore, React can distinguish which elements should not be touched during reconciliation when you delete one of the nodes
<div>
{entryList.map((entry) => {
return <EntryForm key={entry._id} entry={entry} handleEntryDelete={handleEntryDelete} />;
})}
</div>;
Im passing some data with React Context API
and I try to access it from inside a recompose methods
In what way do you access Consumer's data with recompose?
import React from "react";
import { MyContext } from "./index";
import { fromRenderProps, withProps, compose } from "recompose";
const enhance = compose(
/**
* #todo add 'Mr.' to each name
*/
withProps(/** How do I get "names" from Consumer here? */)
);
const GrandChild = props => {
return (
<MyContext.Consumer>
{names => {
console.log(names)
return (
<div>
<h2>GrandChild</h2>
{names.map((name, index) => (<li key={index}>{name}</li>))}
</div>
);
}}
</MyContext.Consumer>
);
};
export default enhance(GrandChild);
live code:
https://codesandbox.io/s/k0xm2vlw8r
Here is one way to solve this:
GrandChild.js
import React from "react";
import { MyContext } from "./index";
import { withProps, compose } from "recompose";
const enhance = compose(
withProps(({ names }) => ({ reshapedNames: ["this first", ...names] }))
);
const GrandChild = props => {
return (
<div>
<h2>GrandChild</h2>
{props.reshapedNames.map((name, index) => (
<li key={index}>{name}</li>
))}
</div>
);
};
const EnhancedGrandChild = enhance(GrandChild);
const EnhancedGrandChildWithContext = props => {
return (
<MyContext.Consumer>
{names => <EnhancedGrandChild names={names} {...props} />}
</MyContext.Consumer>
);
};
export default EnhancedGrandChildWithContext;
Just adds a separate layer to provide the context.
Here's the CodeSandbox: