How to customize nested components? - javascript

How to customize nested components?
I'm building React Native app with Redux.
I have three screens in my app that render list of users.
Follower/Following should show FollowButton:
<FollowersContainer>
<UserList onFollow={func} onUnfollow={func} users={[...]}>
<UserCard user={user} onFollow={func} onUnfollow={func}>
<FollowButton onFollow={func} onUnfollow={func} isFollowing={bool} userId={string} />
</UserCard>
</UserList>
</FollowersContainer>
Ask question screen should show AskButton:
<AskQuestionContainer>
<UserList onAsk={func} users={[...]}>
<UserCard user={user} onAsk={func}>
<AskButton onPress={onAsk} />
</UserCard>
</UserList>
</AskQuestionContainer>
Search Results should not show any button
<SearchResultsContainer>
<UserList users={[...]}>
<UserCard user={user} />
</UserList>
</SearchResultsContainer>
As you can see, all three screens uses UserList and UserCard components.
Currently UserList and UserCard needs to know about onFollow onUnfollow and onAsk actions and how to pass them around.
This can get complicated and not very flexible.
Ideally I want to do something like:
<UserList
rowComponent={
<UserCard button={<FollowButton />} />
}
/>
But how do I pass the actions from the top level component into the actual button? and How do I know which actions to pass?
I could use connect on the actual buttons to pass them the actions directly but I prefer these components stay pure and flexible.
Any suggestion on how to solve it in a clean way?
Thanks,
Ran.

You can pass components as a prop.
render() {
var passedButton = (<FollowButtton />);
var row = (<UserCard button={passedButton} />);
return (
<UserList rowComponent={row}/>
);
};

Related

How to implement data update from one component which might affect other components

I am implementing a todo application which has several pages where each page displays a filtered list of all todos depending on the todos' references.
In addition, there is a global "Add task" button and modal from which users can add a task with references. Depending on which page the user is on when adding a task, this newly added task must either be shown right away because the reference is included in currently active page's filter or it must not be shown.
For example, if the user is on the page "Project 1"
If user adds task for Project 1 -> show on page
If user adds task for Project 2 -> don't show on page
Currently, App.js looks something like this
function App() {
return (
<Page>
<Router>
<Route path="/project1" element={<TaskListProject1 />} />
<Route path="/project2" element={<TaskListProject2 />} />
<Router />
<AddTaskButtonAndModal />
<Page />
);
}
How do I best set up and manage the state so that the components TaskListProject1 and TaskListProject2 are only updated if the added task must be included there?
I was thinking about adding the task from the AddTaskButtonAndModal component to the data base and then let the currently active component retrieve all their tasks from the data base again. For this, I would introduce a dummy state whose sole purpose is triggering a rerender of components like so
function App() {
const [dummyState, setDummyState] = useState(0);
return (
<Page>
<Router>
<Route path="/project1" element={<TaskListProject1 dummyState={dummyState} />} />
<Route path="/project2" element={<TaskListProject2 dummyState={dummyState} />} />
<Router />
<AddTaskButtonAndModal setDummyState={setDummyState} />
<Page />
);
}
...but that feels like I'm misusing states.
Another idea was to always have a complete list of all tasks in the top level state and let each component filter for their tasks when it is rendered. But then I need to always have all tasks in the state when only a fraction of tasks are actually needed.
As you can probably imagine, I'm new to React, so I might simply miss a basic concept. Thanks for any help!

Best way to change CSS in multiple components of the same component in React

I have a component that i'm using 3 times with different data which I am able to complete with the following code;
<div>
<Container className="mt-5">
<Row>
{author_data.map((authors) => {
return (
<Col key={authors.id} className="pe-5 ps-5">
<Author
key={authors.id}
image={authors.image}
author={authors.author}
description={authors.description}
handleClick={(author) => setAuthor(author)}
/>
</Col>
);
})}
</Row>
</Container>
</div>
);
However Im looking to change the CSS on each component once I click on one of the Author components. something like the below using native JS.
document.getElementById("author1").classList.add("addGrayScale");
document.getElementById("author2").classList.add("addGrayScale");
document.getElementById("author3").classList.remove("addGrayScale");
I have used the useState and useContext hooks but I can't seem to get it to work because the Author component will receive the same props. Should I create separate components for each Author? or is there another way to do this.

Navbar with dynamic content and behaviour

Say I have the following App.js:
App.js
render() {
return (
<div>
<Navbar />
<Router>
<Home path="/" />
<RouteA path="/routeA" />
<RouteB path="/routeB" />
</Router>
</div>
);
}
So I need the <Navbar /> to behave and look differently in the /routeA and /routeB.
For example, there is a back button in the <Navbar />. And when user is in /routeA, clicking the back btn will go back one step in history.
However, when user is in /routeB, not only that clicking back btn will now pops up an alert, we also need to render a component that has updating props inside the <Navbar />.
So the way I go about this is, well, redux: The <Navbar /> is connected to its own slice of state in the store. Then, in my routes, in order to make the navbar dynamic accordingly, I dispatch actions with my function and component to make the <Navbar /> behave and look the way I want. So far so good.
Problem is we can't serialize 'function' and 'component' objects in the store and subsequently localstorage...so like on refresh, they are gone...
So my workaround for the 'function' is I immediately dispatch it in the routes' constructor. Whereas for the 'component', I use Portal in my routes' render method to insert it to my <Navbar />...
My question is how would you implement this differently?
Thank you for reading!
You can send function to route components and call this funciton when component is mounted, you can have functions defined inside NavBar component, and switch based on the page you are on.
updateLayout = (page) => {this.setState({currentPage: page})}
render() {
return (
<div>
<Navbar currentPage={this.state.currentPage} />
<Router>
<Home path="/" render={()=> <ComponentHome updateLayout={this.updateLayout}>} />
<RouteA path="/routeA" render={()=> <ComponentA updateLayout={this.updateLayout}>} />
<RouteB path="/routeB" render={()=> <ComponentB updateLayout={this.updateLayout}>} />
</Router>
</div>
);
}

nav component losing props. React router mistake?

First time using react router and I am losing props passed to my nav component once I render a new route. Maybe losing props is wrong way of explaining it. What problem is on home page when i add items to cart a badge on the nav updates with correct number of items but when i click checkout they disappear. When I click back to home the badge displays correctly again.
I think i must be something simple because the app worked before I added router.
1. Home works
2. No cart items shown:
Notes
— NavMain component not added to a Route because I wanted it displayed on both checkout page and home page
— between the render() and the return code counts amount of items in cart and passes the number to the nav through props note added to state.
I think i did something wrong in App sensing that <.NavMain ... /> needs be in a route?
class App Extends React.Component {
render() {
/* counts number of items in cart does not add to state */
const arrayOfQuantities = this.state.cart.map(item => item.quantity);
const countOfCartItems = arrayOfQuantities.reduce(
(total, current) => (total += current)
);
return (
<BrowserRouter>
<div className="App">
/** Is this the problem? **/
<NavMain
countOfCartItems={countOfCartItems}
onTermSubmit={this.onTermSubmit}
handleSearched={this.handleSearched}
itemsInCartBoolean={this.state.cart.length > 1}
/>
<Route
exact
path="/"
render={() => (
<React.Fragment>
<MainCarousel />
<WatchList
cart={this.state.cart}
addCartItem={this.handleAddCartItem}
watchList={data.products[0].frankMuller}
/>
</React.Fragment>
)}
/>
<Route
path="/checkout"
render={() => <Checkout cart={this.state.cart} />} // used for pricing etc.
/>
</div>
</BrowserRouter>
);
}}
export default App;
Checkout component below, I think i'm meant to pass props through to that right? thats not a bootstrap component thats my component which i imported for the checkout I will try pass some props omg fingers crossed. Will try click some buttons.
export default class Checkout extends Component {render() {
return (
<div>
<NavMain />
<h1 className="main-header">Checkout</h1>
<h5 className="sub-heading">YOUR ORDER</h5>
<OrderList
cart={this.props.cart}
addCartItem={this.props.addCartItem}
removeCartItem={this.props.removeCartItem}
/>
);}}
Syed knew where to look the problem was the Checkout component I added the NavMain component in there when it was already added inside the root App. I simply deleted it from the Checkout and left root alone.
works now ^^ thanks

React.js - ReactDOM.render or can you use inline?

I have started development in REACT and have some small queries on how to use it inline.
I have for example the following code snippet.
ReactDOM.render(
<RitualPromoCode url="/Home/ValidatePromoCode" code="Bongo" location="1" package="17" />,
document.getElementById('promo-code')
);
however when using the above using it within my MVC page is annoying...i would like to be able to within the HTML page use the new "tag" inline...e.g.
<RitualPromoCode url="/Home/ValidatePromoCode" code="Bongo" location="1" package="17" />
How do i go about doing this?
On another note..I want to use multiple components in different parts of my page but don't want to write one HUGE component to use them all together. How can i get one component to update / modify the properties of another on the same page?
Thanks
You can call render in one place. Just create some "main" component which will contain all others.
ReactDOM.render(
<App />,
document.getElementById('promo-code')
);
class App extends Component {
render() {
return (
<div>
<RitualPromoCode url="/Home/ValidatePromoCode" code="Bongo" location="1" package="17" />
<SomeOtherComponent />
<MoreComponent />
<MyComponent />
</div>
);
}
}

Categories

Resources