Is there a way to pass a group of props in react? - javascript

The way I actually use works well but my props keep growing and I wondering if I can group them into one single state and pass it to the child, this is my code:
<Panel
uniqueIDs={uniqueIDs}
userID={userID}
loading={loading}
premium={premium}
percent={percent}
total={total}
until={until}
user={user}
done={done}
/>
After the render I define those variable like so :
let { loading, empty, total, uniqueIDs, slice, percent, until, ok,
user, premium, data, keys, done, userID } = this.state;
Can i just send this.state variable? I made a little bit of research I didn't find any solution to my issue, I know I can use third-party libraries to manage state but I am trying to keep it simple.

you can make an object for the props you are sending and can use spread operator
let props = {
uniqueIDs : uniqueIDs,
userID:userID,
loading:loading,
premium:premium
}
<Panel {...props} />
In panel component you can use this.props.uniqueIDs ans so on.

Yes, you definitely could just pass your entire state-variable into your child-component as multiple properties
<Child {...this.state}/>
This would be perfectly acceptable.
If your state is {id: 1, name: 2}
You would still be able to access the props in your child-component as
props.id or this.props.id
props.name or this.props.name
As a note you should be cognizant of component re-rendering. If you make many updates to the state in your parent-component this will also cause your Child component to re-render a lot as well, which could have performance issues.
To workaround this, make sure to employ methods like componentDidUpdate() for class-components and react-hooks for functional components. These can help control the flow of re-rendering.

Related

What is more prefered way of updating data in child components hooks or redux?

i am new to JS.
And I wondered what is more prefered way for updating data in children components.
Is that better to use hooks or redux?
Let say i have a parent component with 3 child components.
<Parent>
<ChildA/>
<ChildB/>
<ChildC/>
</Parent>
And i have a need to update something in ChildA and ChildB and pass it to ChildC
What would the best way to do it ?
Shall i use hooks like this?
[childAValue, setChildAValue] = useState([])
[childBValue, setChildBValue] = useState([])
<ChildA onChildChanged={(e) => {
setChildAValue(e)
}}
/>
<ChildB onChildChanged={(e) => {
setChildBValue(e)
}}
/>
<ChildC childAValue={childAValue} setChildBValue={setChildBValue} />
Or it's better to use redux ?
Because with using hooks i may have a lot of rerendering if there are 3-4 components or more.
But on the other hand Redux will be just overloaded if i will use it for every possible component
Which way do you prefer more and why?
This question also refears to modals also when you need to pass value from modals to suibling component/s.
here you have multiple child components, you need to pass down functions to change them in all of them, redux can be used here and it will remove the hassle pass down props to each of the components. Redux is industry standard and is being used in large web projects, where you need to manage a global state to be present and accessible in all of the components/modules, for example Authentication state,
for your question simply passing down as props also works so there is no need to complicate things further.

How to test if react function component re-renders with react-testling-library

I am working on an app with a large vertical list of child components. Each child component is using React.memo and I've built optimizations to ensure they don't re-render when other components in the list are modified and update state higher up in the component tree.
This is working well and has dramatic performance improvements, however I want to write unit test(s) to prevent future regressions as people on the project add more functionality, props, state etc.
Ideally I would like to render the parent component and with a few of these child components, and do an action on one which should only cause the parent component and that one child component to re-render. I would then like to test that the other child component(s) do not re-render. This will be a safety test to ensure people don't make changes that break the React.memo (anonymous functions or new objects as props etc).
Here's a rough description of the hierarchy:
function Parent() {
const [state, setState] = useState({});
const items = queryItems();
return (
<div>
{items.map((item) => <Child key={item.id} item={item} setState={setState} />)}
<div>
);
}
const Child = React.memo(function({item, setState}) {
const handleClick = () => {
// Does something to update state
setState({something: 'changed'});
};
return (
<div>
{item.title}
<button onClick={handleClick} />
</div>
);
});
If someone were to come in and change the .map logic to this:
{items.map((item) => <Child key={item.id} item={item} setState={setState} otherProp={{}} />)}
That object added as otherProp would break the React.memo and cause all the children to re-render every time the parent renders. How can I write a test to ensure it will fail if someone causes a regression like this? (we have jest, react-testing-library and enzyme available)
One thing you can try is to wrap the child in a HOC with a counter prop to see if it rendered the child, however I'm not sure that would work with memo.
You can try jest's spyon mock object, https://jestjs.io/docs/jest-object, however again I'm not sure that's going to work with memo because it might trigger a rerender on the mock object when it would not have done so on the memo child.
You might have to carefully consider whether you're still doing unit testing when you move into a realm of this sort of testing. Optimization, load balancing, stress testing, how components interact with others and similar ideas fall into the realm of integration testing, as mentioned in the comment to your question. Ideally you should only be testing quantified reusable pieces of code or components and not how they interact with one another, but again that's a grey zone since the parent is related to its own children. In this case however testing whether or not it rerenders, I believe, is not applicable to unit testing.
I believe your best option is just to trust that the child will not rerender if you've passed it the same props and if the memo is applied, in which case you don't have to care about whether it's being rerendered, instead you can test if the props passed to it are still the same and through that conclude that it won't rerender. Rather test something like the props itself instead of the render.

React - Change state from external component

I know that I will ask a question that brake some rules about the core/basic way to use React... but maybe with this example, someone helps me to solve the problem that I facing.
This is not the full code of my project, but show exactly the idea of my problem:
https://codesandbox.io/s/change-state-from-external-component-zi79e
The thing is I need to change a state from a child component from the parent component, but I don't want to run a render method in my parent or handle the state in the parent component.
Exists a way to achieve this? In my project, I have a parent that creates multiple generic children and it will be more difficult to handle this request.
And specifically, I need to change the state of one child (MyFirstChild), after another child (SecondChild) read the keystroke and run an API to get some values from my backend; after that, I need to send the change to "MyFirstChild" to change his state.
The parent component has ~50 child components and I blocked the re-render method (With the method shouldComponentUpdate)
The expected answer is: "It's not possible, or, you broke the good use of React"...
But, maybe using forwardRef or ref, or something else that I not see can help me to work around this...
To change the state of a child component from the parent without having to run a render method (in the parent): one possible solution would be to use Redux. With Redux from the parent you can dispatch an Action (execute an Action that changes the state of Redux).
And in the child component, you receive that part of the state that you change (it could be a string, object, etc) as a prop. So when it is changed from the parent, your child component will render again without having to run a render method in the parent.
https://react-redux.js.org/introduction/basic-tutorial
You could also use Context for the same purpose.
However, I saw your code example, I am not sure the exact reason of why you don't want to make a render in the parent, but the easiest solution for what you need would be to send the function that you want to execute in the parent as a prop and also the title.
If you want to change things from the parent, without re-rendering again with Redux, it would be something like this:
In the parent
const changeTitle = (newTitle) => {
this.props.setTitle(newTitle);
}
return (
<div className="App">
<ChildComponent />
</div>
);
const mapDispatchToProps = dispatch => ({
setTitle: newTitle => dispatch(setTitleACTION(newTitle)),
});
In the child
return (
<div>
<h1>
{this.props.title}
</h1>
<h2>Start editing to see some magic happen!</h2>
</div>
);
const mapStateToProps = ({ title }) => ({
title,
});
export default connect(mapStateToProps, null)(ChildComponent);
Again if you make this with Redux, you can get the "title" prop from the Redux store, and in the parent, you will change that variable (calling to a Redux action) without rendering the parent again.
If you want to fire the event from the child component, you can dispatch the action from the child component or you could call a function (that you receive from props from the parent) and call that function whenever you need.
Can't we use props here for passing data instead of trying to manipulate state from outside of component?
Based on #david paley explanation...
If the only way to achieve this is using Redux, I post my solution (It's the same example, but, implementing Redux)... hope that works for anyone else.
https://codesandbox.io/s/change-state-from-external-component-redux-rmzes?file=/src/App.js

Is there a better way to pass multiple props down to n children

Currently working a group project and I've been looking over the code the group wrote and I'm curious if there is a better way to pass props to n children components that change the state of the top-level App component.
currently we have something like this:
return (
<div>
<header className="navBar">
// NavBar is an example for the question
<NavBar
showSplash={this.state.showSplash}
displayAllSearchResults={this.displayAllSearchResults}
searchBarDisplay={this.state.showCard}
updateResults={this.updateResults}
selectResult={this.selectResult}
searchResults={this.state.searchResults}
showSuggestions={this.state.showSuggestions}
/>
</header>
<section className="App">
<article className="mainContent">{card}</article>
</section>
<div className="appBackground" />
</div>
);
All of these methods (which there are probably too many) are being passed into the NavBar component that itself is composed with a SearchBar component which also needs that giant block passed into it in order to change the state all the way back at the top-level App. This feels wrong, is there a solution or better approach to passing all these props down?
You can use the spread operator like this :
<NavBar {...this.state} />
It will pass you state properties as props to the navbar.
In you Navbar component, you can get the value of showSplash in state from props.showSplash
If you want to pass the data into a child component of Navbar, you can do something like this :
<ChildComponent {...props} /> // if Navbar is a functional component
<ChildComponent {...this.props} /> // if Navbar is a stateful component
Reference:
Spread in object literals
Here is a sample stackblitz for illustration.
If you want a data to be accessible by all the child components, then you have following options:
React Context
Redux or state management library like flux etc.
Or you could follow #Abdelkarim EL AMEL answer if that soughts out you issue for now.I would suggest take a look at the above mentioned options and if setting a global state which can be accessed anywhere in the app helps you to not send prop to each component explicitly. I would prefer to use a global state for variable which are common and needs to be accessed by all the child of the root app.
For example a button component to change the theme of the app. It changes the state of the root app and is accessible by every component.
I think this is actually entirely OK, and would recommend leaving it.
If you don't like seeing it itemized in the return of the render method you could keep them all in an object called navbarprops and just spread that in. I still prefer it as is though!

What's the proper way of passing a ref to a prop?

I'm trying to pass a ref of a component to another component. Since string refs are being deprecated I'm using callback refs.
So I have something similar to this:
<One ref={c => this.one = c}/>
<Two one={this.one}/>
The problem is that whenever I try to access this.props.one inside Two I get undefined.
I have even tried this on Two:
componentDidMount(){
setTimeout(()=>{
console.log(this.props.one);
},5000)
}
It seems the problem is that when the prop is created, the ref doesn't exist yet since it's created once One is mounted. But I don't know how to "refresh" the props on Two to get the ref to the mounted component.
So what's the proper way of passing a ref to another component?
Edit
Some users have suggested to encapsulate that logic in a higher component, which in itself renders those other child components.
The problem with that approach is that you can't create reusable logic and you have to repeat the same logic over and over in those encapsulating components.
Let's say you want to create a generic <Form> component which encapsulates the submit logic to your store, error checking, etc. And you do something like this:
<Form>
<Input/>
<Input/>
<Input/>
<Input/>
<SubmitButton/>
</Form>
In this example <Form> can't access the instances (and methods) of the children since this.props.children doesn't return those instances. It returns some list of pseudo components.
So how can you check if a certain <Input/> has detected a validation error without passing a ref?
You have to encapsulate those components in another component with the validation logic. For example in <UserForm>. But since each form is different the same logic has to be copied in <CategoryForm>, <GoupForm>, etc. This is terribly inefficient which is why I want to encapsulate the validation logic in <Form> and pass references of the <Input> components to <Form>.
In general the "ref" feature is an anti-pattern in React. It exists to enable side-effect driven development, however in order to benefit the most from the React way of programming you should try to avoid "refs" if possible.
As for your particular issue, passing a child a ref to it's sibling is a chicken vs. egg scenario. The ref callback is fired when the child is mounted, not during render which is why your example doesn't work. One thing you can try is pushing the ref into state and then reading from state into the other child. So:
<One ref={c => !this.state.one && this.setState({ one: c })}/>
<Two one={this.state.one}/>
Note: without the !this.state.one this will cause an infinite loop.
Here is a codepen example of this working (look at the console to see the sibling ref logged): http://codepen.io/anon/pen/pbqvRA
This is now much simpler using the new ref api (available since React 16 - thanks to perilandmishap for pointing that out).
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.oneRef = React.createRef();
}
render () {
return (
<React.Fragment>
<One ref={this.oneRef} />
<Two one={this.oneRef} />
</React.Fragment>
}
}
}
You would consume the prop in Two like:
this.props.one.current
A few things of note with this approach:
The ref will be an object with a current property. That property will be null until the element/component is mounted. Once it's mounted, it will be the instance of One. It should be safe to reference it once <Two /> is mounted.
Once the <One /> instance is unmounted, the current property on the ref returns to being null.
In general, if you need to pass a reference to something that may not be set at call time, you can pass a lambda instead:
<One ref={c => this.one = c}/>
<Two one={() => this.one}/>
and then reference it as
this.props.one()
If it has been set when you call it, you'll get a value. Before that, you'll get undefined (assuming it hasn't otherwise been initialized).
It bears noting that you won't necessarily re-render when it becomes available, and I would expect it to be undefined on the first render. This is something that using state to hold your reference does handle, but you won't get more than one re-render.
Given all that, I would recommend moving whatever code was using the ref to One in Two up into the component that is rendering One and Two, to avoid all the issues with both this strategy, and the one in #Carl Sverre's answer.

Categories

Resources