I have a function component like that
function myMainComponent() {
const MyUIA() {
//some calulations about attrs
return (<div><ComponentA {...attrs}></div>)
}
const MyUIB() {
//some calulations about attrs
return (<div><ComponentA {...attrs}></div>)
}
// Way A
/*
return (
<div>
<MyUIA/>
<MyUIB/>
</div>);
*/
// Way B
return (
<div>
{MyUIA()}
{MyUIB()}
</div>);
}
The results of render by WayA and WayB is the same, but in first case there is a flickering
So why the WayB has better performance without flickering ?
What is the best way to render part of renders thas exist in the same function component ?
does WayA and WayB have a specific name for example "render by calling native method" and "render by calling JSX method" in react ?
Since as we know every code we write in react is converted into JSX first, then it get rendered so if we see for native method the JSX element is created twice while for JSX it's created only once. So this is the case one can see the flickering. I hope it helps.
function myMainComponent() {
const MyUIA = () => {
return /*#__PURE__*/React.createElement("div", null,/*#__PURE__*/React.createElement(ComponentA, null)); };
const MyUIB = () => {
return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(ComponentA, null));};
// Way A
return /*#__PURE__*/React.createElement("div", null,
/*#__PURE__*/React.createElement(MyUIA, null),
/*#__PURE__*/React.createElement(MyUIB, null));
// Way B
return /*#__PURE__*/React.createElement("div", null, MyUIA(), MyUIB());
}
Related
So I have this Display() function which fetches events from the Google Calendar via an API and store each event's name via element.summary into the events set. And then once the events set is populated, I iterate through the set via for (let item of events) and create a new <a> tag for each event/item in the set using the name as the text via <a>{item}</a> (for e.g. <a>call<a>, then I push each <a> tag into a new array called tabs and then finally return the tabs array. The events set contains three items and when I console.log, I see the correct items ("call", "kist", & "go") in the set. However, once I console.log the tabs array, it only contains one <a> tag whose value is null whereas it is supposed to contain three <a> tags since it iterates through the events set which has three items and is supposed to create an <a> tag for each. Also, I get the error that item is not defined for the line for (let item of events), somehow I cannot iterate through the events set. See console output here.
function Display() {
let events = new Set()
let tabs = []
ApiCalendar.listUpcomingEvents(10)
.then(({result}: any) => {
result.items.forEach(element => {
console.log(element.summary)
events.add(element.summary)
}
);
console.log(events)
for (let item of events)
console.log(item)
tabs.push(<a>{item}</a>)
console.log(tabs)
return tabs
});
}
This is the class that I made in the same file as the above function, which basically renders a 'Log In' button if user is not logged in to their calendar, or renders the array of <a> tags returned by the Display() function if user is already logged in. However, even though the Display() function above does return something (i.e. an array of <a> tags) and the render() function inside the class also returns a <div> element with the corresponding component inside the div, I get the error Uncaught Error: Display(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null. I am new to JavaScript and have no idea what I'm doing wrong. Any help is greatly appreciated and thank you in advance.
export default class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {
sign: ApiCalendar.sign,
};
}
render() {
const isLoggedIn = this.state.sign;
let ele;
if (isLoggedIn) {
ele = <Display/>;
} else {
ele = <Button>'Sign In'</Button>;
}
return (
<div>
{ele}
</div>
);
}
}
Your Display function calls an async method and returns nothing. You will need to utilize state and effect inside Display to render returned data. But then, you will encounter errors if user navigates away from page before data is fetched.
Best solution for this problem would be to utilize redux and redux-thunk
Caution, untested code below
If you feel like you don't need redux, try this approach
async function fetchItems() {
const result = await ApiCalendar.listUpcomingEvents(10);
return result.result.items.map(({summary}) => summary);
}
function Display() {
const [items, saveItems] = useState([]);
const isMounted = useRef(true);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
(async () => {
const items = await fetchItems();
//Do not update state if component is unmounted
if (isMounted.current) {
saveItems(items);
}
})();
}, []);
return <>{items.map(item => <a key={item}>{item}</a>)}</>
}
If you want to render more than summary, you can do it like this
async function fetchItems() {
const result = await ApiCalendar.listUpcomingEvents(10);
return result.result.items.map(({summary, somethingElse}) => ({summary, somethingElse}));
//can be replaced with return [...result.result.items]; to get all props
}
function Display() {
//... Main logic of Display component is the same,
//so I wouldn't duplicate it here
return <>{items.map(item => <div key={item.summary}>{item.summary} {item.somethingElse}</div>)}</>
}
It seems that you are not returning anything on Display component.
You can't return a promise on a component so you need to make it inside useEffect using react hooks or component lifecycle - and no, you don't need redux just to achieve this.
function Display() {
let events = new Set()
let tabs = [];
const [items, setItems] = useState([]);
const getList = async () => {
const res = await ApiCalendar.listUpcomingEvents(10);
setItems(res.items);
}
useEffect(async () => {
getList();
}, []);
return items.map(item => <div>{item}</div>);
}
I have code where if a function is invoked it will call toggleCheckedUser and pass along information about which object property to toggle. Then saves the modified object back to state (selectedSendTo).
However, when I run this, the toggle it works, but when I try to edit a second property, before changing it I try console.log(selectedSendTo) I always get the initial value whether it be an empty object {} or false instead of the previously updated object.
When I use useEffect to spy on selectedSendTo I can see that the setSelectedSendTo() function correctly updated the object. So for some reason when I revisit the object it's empty.
const [selectedSendTo, setSelectedSendTo] = useState(false);
const toggleCheckedUser = (companyID, contactID) => {
console.log(companyID, contactID);
console.log(selectedSendTo); // THIS VALUE IS always the same as INITIAL value
console.log(selectedSendTo[companyID]);
if(selectedSendTo[companyID] &&
selectedSendTo[companyID][contactID] === true){
//remove it
delete(selectedSendTo[companyID][contactID]);
}else{
setSelectedSendTo({
...selectedSendTo,
[companyID]:{
...selectedSendTo[companyID],
[contactID]: true,
}
})
}
}
Here is the DOM:
<CustomCheckbox
className="person__checkbox" name={`checkbox-${contactID}`}
alreadySelected={
selectedSendTo[company.companyID] &&
selectedSendTo[company.companyID][contactID]
}
onChange={() => toggleCheckedUser(company.companyID, contactID)}
/>
UPDATE, A POSSIBLE SOLUTION
I found that the following works:
To be able to access the current value from useState I used useRef
const selectedSendToRef = useRef();
useEffect( () => {
selectedSendToRef.current = selectedSendTo;
}, [selectedSendTo])
Then inside of my function, I can use selectedSendToRef.current to access the most recent value of `selectedSendTo.
When updating state, I can access the most recent version from state using
setSelectedSendTo( prevValue => ....)
const toggleCheckedUser = (companyID, contactID) => {
console.log(companyID, contactID, selectedSendToRef.current);
console.log('selectedSendTo[companyID]: ', selectedSendTo[companyID]);
let newValue;
if(selectedSendToRef.current[companyID] &&
selectedSendToRef.current[companyID][contactID] === true){
newValue = false;
}else{
newValue = true;
}
setSelectedSendTo(prevValue => (
{
...prevValue,
[companyID]:{
...prevValue[companyID],
[contactID]: newValue,
}
}
));
}
UPDATE 2: The Real Solution
Okay so it seems like the problem was that even after a render, the child component was not receiving the updated state because of how I had used nested functions to create the elements.
Here is how I had things
<Main Component>
<div>
{Object_1}
<div>
</Main Componenent
and object_1 was defined something like this:
const Object_1 =
<React.Fragment>
<h1>Random Header</h1>
{StateObject_Containg_Elements}
</React.Fragment>
Now to create the state object that conatined the elements I wanted to display I was using a funciton called by a useEffect hook. Basically when the server sent back data that I needed, I would tell the useEffect hook to run a function called createElements
const createElements = (data) => {
const elements = Object.keys(data).map( item => return(
<ul>
{subFunction1(item)}
</ul>
subFunction1(item){
item.contacts.map( name => {
reutrn <CustomCheckbox name={name} checked={selectedSendTo[name]}
})
}
saveElementsToState(elements);
}
As you can see we basically have a function that runs 1 time (on server response) that triggers a function that creates the array of elements that we want to display which has its own nested subfunction that includes the child component that we are asking to watch a different state object to know whether it should be checked or not.
So What I did was simplify things, I turned {Object_1} into it's own functional component, lets call it <Object1 />. Inside the component instead of calling a function I just put the function code in there to loop through and return the elements (no longer saving elements to state) and lastly I no longer needed the useEffect since just updating the state object with the data once it gets it from the server would cause my subcomponent to re-render and create the elements. Inside the sub-component I simply return null if the data in state is null.
That fixed all my problems.
so now it looks something like this:
const Object1 = () => {
if(!data)return null;
return(
Object.keys(data).map( item => return(
<ul>
{subFunction1(item)}
</ul>
subFunction1(item){
item.contacts.map( name => {
reutrn <CustomCheckbox name={name} checked={selectedSendTo[name]}
})
}
)
}
return(
<div>
<Object1 /> //This is what contains/creates the elements now
</div>
)
I have a function in my parent component which is sent to the child components as a prop.In one of my child component,I want the same function(which was sent as a prop from the parent component) to be run twice.The first will be run with some arguments and return the value from that particular child component.The second will be to pass the same function(which came from the parent component,not the one executed in this component) to a separate component as a props again.So my function is:
//Function defined in the parent component and sent as props to the child components
handleShelfChange = (bookOnChange, newSehlf) => {
!this.isTheBookNew(bookOnChange) && this.setState(state => {
let newBooks = state.books.map(book =>
{ if (bookOnChange.id === book.id)
{ book.shelf = newSehlf; }
return book;
});
return {
books: newBooks
};
}
);
BooksAPI.update(bookOnChange, newSehlf);
};
I am sending this function to one of the child component as shown below:
<BookSearch
books={this.state.books}
handleShelfChange={this.handleShelfChange}/>
Now, in my BookSearch Component,I need to perform 2 actions:
1) Use handleShelfChange() method and return values from BookSearch Component.
2) Pass handleShelfChange() method to another component as props which will use the same method again and return some values.
So,I am facing some issues in the first point, I am using the function as a callback to this.setState as
this.setState({ books : book_search }, this.check()); // callback function to this.setState
and the callback function is as shown below:
check() {
let parent_books = this.props.books;
let shelf;
console.log(this.state.books)
const book_result = this.state.books.map((book) => {
const parent = parent_books.find(parent => parent.title === book.title );
if(parent) {
console.log(parent);
book.shelf = parent.shelf;
let shelfChange = this.props.handleShelfChange(book,book.shelf) //Call to the
//function sent as props from the parent component
shelf = shelfChange
}
console.log(shelf) // undefined
return [book,shelf];
})
//console.log(book_result);
this.setState({books: book_result})
console.log(this.state.books)
}
So,I need to run the handleShelfChange() function here for all the books that satisfy the if condition and return it from the check method(the callback method). So, I tried to declare a variable outside the if condition and assign the output of the function to that variable(declared outside the if condition) .I also need to return the each book from the map function satisfying the condition.So,I have used an array to return both the values. But as I have mentioned in the comment, console.log(shelf) return an empty array.It shows undefined.What is the correct way to get both the values from the callback function? Can anyone please suggest me a solution?
Your problem arises because you are using the setState callback syntax incorrectly. The second argument to setState is a callback function you are just executing the function
You need to write it like
this.setState({ books : book_search }, this.check);
Also console.log() after setState doen't show you the updated state values, you should write it in the callback
this.setState({books: book_result}, () => {console.log(this.state.books)})
Also make sure that you are returning the appropriate value from the handleSelfChange function
I'm not quite sure where to initiate calculated values in a react component. So this is how I am doing it right now:
render () {
const { foo } = this.props
const { bar } = this.state
const calculatedValue = bar ? foo * bar : foo
return (
<div>{calculatedValue}</div>
)
}
Or should I initiate calculatedValue in componentDidMount()
I think it really depends on how much calculation you are doing. For something as simple as your example above I would usually just do that in render().
Anything above very basic functionality and I would split it out into a separate function such as getFooBarCalcValue().
This way your render method doesn't get too cluttered with things that would otherwise be elsewhere.
This depends on how your calculated value is going to change in your application. If is a value that will not change, your just wanna when the first render of the component (what you probably not), you're fine calculating in componentDidMount and assigning to a property (this.calculatedValue = ...) and accessing in render with this.calculatedValue, but it's not the "React way".
Assuming you're using the state, something that will be change in your application, then you will need to put your calculation in somewhere else.
Here are some options to you to keep your calculated values:
1. In your render method, just like you did:
Fine for simple calculations, but believe me, this thinks can grow...
render () {
const { foo } = this.props
const { bar } = this.state
const calculatedValue = bar ? foo * bar : foo
return (
<div>{calculatedValue}</div>
)
}
2. In a getter method:
keeping the calculation separated for your render method
getCalculatedValue() {
const { foo } = this.props
const { bar } = this.state
return bar ? foo * bar : foo
}
render () {
return (
<div>{ this.getCalculatedValue() }</div>
)
}
3. [ES6] With a getter property:
Just a variation for the option above, but a little more cleaner since your aren't calling a method, just accessing a property and your function is running "under the table".
get calculatedValue() {
const { foo } = this.props
const { bar } = this.state
return bar ? foo * bar : foo
}
render () {
return (
<div>{ this.getCalculatedValue }</div>
)
}
4. In the parent component, making this one just to show the value:
This one is a little more complex, but usually is better for growing projects. Here we will separate your component in two: The first will calculate (here you can access some API or anywhere your value was coming from) and the child one will just show it's value. This way you can keep the logic and the ui in different components, improving your reuse of code among your project. Is a good pattern to keep your UI components (the child one) in a functional component without state, but this is for another question.
/* A functional component. Is just like a class, but without state */
const MyUIComponent = props => <div>{ props.value }</div>
class MyLogicalComponent extends React.Component {
render(){
/* you can choose any of the options above to calculate */
const computedValue = 5 + 8;
return (
<div>
<MyUIComponent value={ computedValue } />
</div>
)
}
}
Hope it helps!
calculatedValue is a const variable having block scope in your Render function. If you need to use its value in Render function there is no option but to calculate it in Render function only.
componentDidMount = () => {
const { foo } = this.props
const { bar } = this.state
const calculatedValue = bar ? foo * bar : foo //block scope
}
If you declare the variable calculatedValue in componentDidMount() function as shown above then it will not be accessible in Render function which gets called later in the lifecycle of a reactJs component.
render = () => {
//calculatedValue will give undefined error. calculatedValue is not known to render function
return (
<div>{calculatedValue}</div>
)
}
I'm using React to display a Material Design Grid List of news articles. Im passing the JSON I receive to this GridList component (this.props.newsArticles), and mapping through each returned result so I can filter the data based on whether it has a hi-def image, then sending the data to MediaTile to get rendered. I'm having a problem with my ternary in GridList module's hasHiDefPictures function. I receive a syntax error.
const Components = {
MediaTile: React.createClass({
render: function() {
return (
<GridTile
title={this.props.tile.title}
>
<img src={this.props.tile.multimedia[0].url} />
</GridTile>
)
}
}),
GridList: React.createClass({
render: function() {
var newsArticles = this.props.newsArticles
var renderArticles = newsArticles.data.results.map(function(tile, key) {
return hasHiDefPictures(tile, key)
})
function hasHiDefPictures(tile, key) {
return {tile.multimedia > 3 ? <Components.MediaTile key={key} tile={tile}/> : ""}
};
return (
<div>
<GridList>
{renderArticles}
</GridList>
</div>
);
}
})
}
Now the only way to get around this syntax error is to wrap that returned value in div's like so:
function hasHiDefPictures(tile, key) {
return (
<div>
{tile.multimedia > 3 ? <Components.MediaTile key={key} tile={tile}/> : ""}
</div>
)
};
But I do not want to do that. Does anyone have advice on how to get around this problem? I'm still new to react, so there's a good chance that I'm just not handling the data in the proper place. Any help would be appreciated! Thanks!
You just need to remove the {} around your ternary. {} is useful in JSX (an expression starting with <) to evaluate some JS code, but your array mapping already occur in pure JS code (the beginning of the render function) so it has the regular JS meaning: a block or an object literal.