Passing props to a Function as a Child component - reactjs

This works, but I need to separate out the cellRenderer component.
// Grid.js
import React, { Component } from "react";
class Grid extends Component {
render() {
const index = 3;
return (
<div style={{ height: "5em", width: "6em", border: "1px solid black" }}>
{this.props.text}
{this.props.children({ index, cellText: "no." })}
</div>
);
}
}
export default Grid;
And App.js. If I click on "no.3", it correctly logs "x: 6"
import React, { Component } from "react";
import Grid from "./Grid";
class App extends Component {
constructor(props) {
super(props);
this.state = {
x: 5
};
}
handleIncrement = () => {
this.setState(
state => ({ x: state.x + 1 }),
() => console.log(`x: ${this.state.x}`)
);
};
cellRenderer = ({ index, cellText }) => {
return <div onClick={() => this.handleIncrement()}>{cellText + index}</div>;
};
render() {
return (
<div className="App">
<Grid text={"Hello "}>{this.cellRenderer}</Grid>
</div>
);
}
}
export default App;
Now, if I have to separate out cellRenderer component as below, how can I pass the handleIncrement function to it ?
import React, { Component } from "react";
import Grid from "./Grid";
const cellRenderer = ({ index, cellText }) => {
return <div>{cellText + index}</div>;
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
x: 5
};
}
handleIncrement = () => {
this.setState(
state => ({ x: state.x + 1 }),
() => console.log(`x: ${this.state.x}`)
);
};
render() {
return (
<div className="App">
<Grid text={"Hello "}>{cellRenderer}</Grid>
</div>
);
}
}
Edit:
This works:
// pass handleIncrement to Grid
<Grid text={"Hello "} handleIncrement={this.handleIncrement} >{cellRenderer}</Grid>
// And within Grid, pass it to cellRenderer
{this.props.children({ index, cellText: "no.", handleIncrement: this.props.handleIncrement })}
// Update cellRenderer to this
const cellRenderer = ({ index, cellText, handleIncrement }) => {
return <div onClick={handleIncrement}>{cellText + index}</div>;
};
But the problem is that Grid is a component from the library react-window, and I cannot override the library code. Any other way possible ?

The fact that you have separated our cellRenderer as a functional component, you could render it as a component and pass on the props
const CellRenderer = ({ index, cellText, handleIncrement }) => {
return <div onClick={handleIncrement}>{cellText + index}</div>;
};
...
return (
<div className="App">
<Grid text={"Hello "}>{({index, cellText}) => <CellRenderer handleIncrement={this.handleIncrement} index={index} cellText={cellText}/>}</Grid>
</div>
);

This will work:
import React, { Component } from 'react'
import Grid from './components/Grid'
const CellRenderer = ({ index, cellText, handleIncrement }) => {
return <div onClick={handleIncrement}>{cellText + index}</div>
}
class App extends Component {
constructor(props) {
super(props)
this.state = {
x: 5
}
}
handleIncrement = () => {
this.setState(
state => ({ x: state.x + 1 }),
() => console.log(`x: ${this.state.x}`)
)
}
render() {
return (
<div className="App">
<Grid text={'Hello '}>
{props => (
<CellRenderer {...props} handleIncrement={this.handleIncrement} />
)}
</Grid>
</div>
)
}
}
export default App

Related

cannot display item by passing value from another component

I wanna display my cart item inside my CartList component but for some reason, it doesn't work out...Is there anything wrong with the way i destructure the cart variable able or anything? please help me! Thank you so much!
context.js:
class ProductProvider extends React.Component {
state = {
products: storeProducts,
detailProduct: detailProduct,
cart: storeProducts,
modalOpen: false,
modalProduct: detailProduct
};
getItem = (id) => {
const product = this.state.products.find((item) => item.id === id);
return product;
};
addToCart = (id) => {
let tempProducts = [...this.state.products];
const index = tempProducts.indexOf(this.getItem(id));
const product = tempProducts[index];
product.inCart = true;
product.count = 1;
const price = product.price;
product.total = price;
this.setState(() => {
return (
{ products: tempProducts, cart: [...this.state.cart, product] },
() => console.log(this.state)
);
});
};
openModal = (id) => {
const product = this.getItem(id);
this.setState(() => {
return { modalProduct: product, openModal: true };
});
};
closeModal = (id) => {
this.setState(() => {
return { modalOpen: false };
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
addToCart: this.addToCart,
openModal: this.openModal,
closeModal: this.closeModal
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
Cart.js:
import React from "react";
import CartColumns from "./CartColumns";
import CartList from "./CartList";
const ProductContext = React.createContext();
export default class Cart extends React.Component {
render() {
return (
<div>
your cart
<ProductContext.Consumer>
{(value) => {
if (value && value.length > 0) {
return (
<div>
<CartColumns />
<CartList value={value}/>
</div>
);
}
}}
</ProductContext.Consumer>
</div>
);
}
}
CartItem.js:
import React from "react";
function CartItem() {
return <div>this is a cart item</div>;
}
export default CartItem;
CartList.js:
import React from "react";
import CartItem from "./CartItem"
export default class CartList extends React.Component {
{value =>{
return <div>
{cart.map((item) => {
return (
<CartItem key={item.id} value={value} item={item}/>
)
}}
</div>;
}}
}
export default CartList;
Sandbox link: https://codesandbox.io/s/cart-code-addict-buz0u?file=/src/cart/CartList.js
first of all fixed your cartList component
import React from "react";
import CartItem from "./CartItem"
export default function CartList (props) {
const {cart} = props
return (
<div>
{cart.map((item) => (
<CartItem key={item.id} item={item} />
))}
</div>
)
}

How to set zustand state in a class component

I am working on a site that has a piece a global state stored in a file using zustand. I need to be able to set that state in a class component. I am able to set the state in a functional component using hooks but I'm wondering if there is a way to use zustand with class components.
I've created a sandbox for this issue if that's helpful:
https://codesandbox.io/s/crazy-darkness-0ttzd
here I'm setting state in a functional component:
function MyFunction() {
const { setPink } = useStore();
return (
<div>
<button onClick={setPink}>Set State Function</button>
</div>
);
}
my state is stored here:
export const useStore = create((set) => ({
isPink: false,
setPink: () => set((state) => ({ isPink: !state.isPink }))
}));
how can I set state here in a class componet?:
class MyClass extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<button
onClick={
{
/* setPink */
}
}
>
Set State Class
</button>
</div>
);
}
}
A class component's closest analog to a hook is the higher order component (HOC) pattern. Let's translate the hook useStore into the HOC withStore.
const withStore = BaseComponent => props => {
const store = useStore();
return <BaseComponent {...props} store={store} />;
};
We can access the store as a prop in any class component wrapped in withStore.
class BaseMyClass extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
const { setPink } = this.props.store;
return (
<div>
<button onClick={setPink}>
Set State Class
</button>
</div>
);
}
}
const MyClass = withStore(BaseMyClass);
Seems that it uses hooks, so in class you can work with the instance:
import { useStore } from "./store";
class MyClass extends Component {
render() {
return (
<div>
<button
onClick={() => {
useStore.setState({ isPink: true });
}}
>
Set State Class
</button>
</div>
);
}
}
Create a React Context provider that both functional and class-based components can consume. Move the useStore hook/state to the context Provider.
store.js
import { createContext } from "react";
import create from "zustand";
export const ZustandContext = createContext({
isPink: false,
setPink: () => {}
});
export const useStore = create((set) => ({
isPink: false,
setPink: () => set((state) => ({ isPink: !state.isPink }))
}));
export const ZustandProvider = ({ children }) => {
const { isPink, setPink } = useStore();
return (
<ZustandContext.Provider
value={{
isPink,
setPink
}}
>
{children}
</ZustandContext.Provider>
);
};
index.js
Wrap your application with the ZustandProvider component.
...
import { ZustandProvider } from "./store";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<ZustandProvider>
<App />
</ZustandProvider>
</StrictMode>,
rootElement
);
Consume the ZustandContext context in both components
MyFunction.js
import React, { useContext } from "react";
import { ZustandContext } from './store';
function MyFunction() {
const { setPink } = useContext(ZustandContext);
return (
<div>
<button onClick={setPink}>Set State Function</button>
</div>
);
}
MyClass.js
import React, { Component } from "react";
import { ZustandContext } from './store';
class MyClass extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<button
onClick={this.context.setPink}
>
Set State Class
</button>
</div>
);
}
}
MyClass.contextType = ZustandContext;
Swap in the new ZustandContext in App instead of using the useStore hook directly.
import { useContext} from 'react';
import "./styles.css";
import MyClass from "./MyClass";
import MyFunction from "./MyFunction";
import { ZustandContext } from './store';
export default function App() {
const { isPink } = useContext(ZustandContext);
return (
<div
className="App"
style={{
backgroundColor: isPink ? "pink" : "teal"
}}
>
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<MyClass />
<MyFunction />
</div>
);
}
If you aren't able to set any specific context on the MyClass component you can use the ZustandContext.Consumer to provide the setPink callback as a prop.
<ZustandContext.Consumer>
{({ setPink }) => <MyClass setPink={setPink} />}
</ZustandContext.Consumer>
MyClass
<button onClick={this.props.setPink}>Set State Class</button>
This worked out pretty well for me.
:
import React, { Component } from "react";
import { useStore } from "./store";
class MyClass extends Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<button
onClick={
useStore.getState().setPink() // <-- Changed code
}
>
Set State Class
</button>
</div>
);
}
}
export default MyClass;
I like to create a high order component similar to redux connect:
function connectZustand(useStore, selector) {
return (Component) =>
React.forwardRef((props, ref) => <Component ref={ref} {...props} {...useStore(selector, shallow)} />);
}
eg:
import React, { Component } from 'react';
import create from 'zustand';
import shallow from 'zustand/shallow';
function connectZustand(useStore, selector) {
return (Component) =>
React.forwardRef((props, ref) => <Component ref={ref} {...props} {...useStore(selector, shallow)} />);
}
const useStore = create((set) => ({
isPink: false,
setPink: () => set((state) => ({ isPink: !state.isPink })),
}));
class MyClass extends Component {
render() {
const { setPink } = this.props;
return (
<div>
<button onClick={() => setPink()}>Set State Class</button>
</div>
);
}
}
const MyClassWithZustand = connectZustand(useStore, (state) => ({ setPink: state.setPink }))(MyClass);
export default function Test() {
const isPink = useStore((state) => state.isPink);
return (
<>
<MyClassWithZustand />
{isPink ? 'Is Pink' : 'Is Not Pink'}
</>
);
}

React Re-Render Component on props Change

I have a Tabbar in my Tabbar Component, Which I Change the index props in it :
class Tabbar extends Component {
state = {
index: this.props.index,
name: this.props.name,
image: this.props.image
};
changeTabs = () => {
this.setState({index: this.props.index});
}
render() {
return (
<React.Fragment>
<div id={this.state.index} className="col">
<button onClick={this.changeTabs}></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And Then In my Other Component, I Wanna Re-Render a fragment after props change. Here's my Code :
import Tabbar from './Tabbar';
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
{index: 0, name: "tab0", image:require('../Assets/profile.svg'),childView: {ProfilePage} },
{index: 1, name: "tab1", image:require('../Assets/home.svg'),childView: {HomePage}},
{index: 2, name: "tab2", image:require('../Assets/blog.svg'),childView: {BlogPage}},
],
}
}
handleRender = () => {
this.state.tabs.map(item => {
if (item.index === this.props.index) {
return <item.childView/>;
}
})
return <BlogPage/>;
}
render() {
return (
<div>
<Header/>
{this.handleRender()}
{this.state.tabs.map(item =>
<Tabbar key={item.index} index={item.index} name={item.name} image={item.image}/>
)}
</div>
);
}
}
export default Tabview;
The Method "handleRender" should handle the rendering.
I tried to use "componentDidMount" or "componentDidUpdate", But I didn't work.
How Can I Make it Work?
Thank you in advance!
You dont need to have a state in the child component for this reason
You can simply have a callback in parent and call it in child component like below.
import React, { Component } from "react";
class Tabbar extends Component {
render() {
return (
<React.Fragment>
<div id={this.props.index} className="col">
<button
onClick={() => this.props.changeTabs(this.props.index)}
></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And in parent you maintain the active index state
import Tabbar from "./Tabbar";
import React, { Component } from "react";
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
//your tabs
],
activeIndex: 0
};
}
handleRender = () => {
this.state.tabs.map((item) => {
if (item.index === this.state.activeIndex) {
return <item.childView />;
}
});
return <div />;
};
render() {
return (
<div>
{this.handleRender()}
{this.state.tabs.map((item) => (
<Tabbar
key={item.index}
index={item.index}
name={item.name}
image={item.image}
changeTabs={(index) => this.setState({ activeIndex: index })}
/>
))}
</div>
);
}
}
export default Tabview;

I have a react app that should render the child component card on click. When trying to pass the child down from the parent, the parent breaks

I'm also using semantic-ui-react. When I pass the child component down from the parent the css styling gets all messed up, I lose my images and the click doesn't work.
I can call the cardClickHandler method in the parent component and am console logging the correct child, i just can't get it to render (am not hitting the console.log in the child component).
I also tried to run the cardClickHandler method in the images container to pass it down but that didn't work.
please help and explain what i'm doing wrong. thanks!
images container:
import React from 'react';
import SearchBar from '../components/SearchBar';
import Images from '../components/Images';
import ImageCard from '../components/ImageCard';
class ImagesContainer extends React.Component {
state = {
images: [],
image: {},
sortValue: '',
inputValue: '',
};
componentDidMount() {
fetch('http://localhost:3000/images').then((resp) => resp.json()).then((resp) => {
this.setState({
images: resp
});
});
}
imageFilterOnChange = (event) => {
this.setState({
inputValue: event.target.value
});
};
sortImages = (images) => {
if (this.state.sortValue === 'location') {
return [ ...images ].sort((a, b) => {
if (a.location > b.location) {
return 1;
} else if (a.location < b.location) {
return -1;
} else {
return 0;
}
});
} else {
return images;
}
};
render() {
const filteredImages = this.state.images.filter((image) => {
return image.location.toLowerCase().includes(this.state.inputValue.toLowerCase());
});
return (
<div>
<Images
images={this.sortImages(filteredImages)}
onClick={this.cardClickHandler}
/>
<SearchBar
images={this.sortImages(filteredImages)}
imageFilterOnChange={this.imageFilterOnChange}
inputValue={this.state.inputValue}
onChange={this.handleSortImages}
/>
</div>
</div>
);
}
}
export default ImagesContainer;
parent component:
import React from 'react';
import ImageCard from './ImageCard';
import { Card, Image } from 'semantic-ui-react';
class Images extends React.Component {
state = {
image: []
};
cardClickHandler = (e) => {
let cardId = e.target.dataset.id;
this.props.images.find((image) => {
return image.id === cardId;
});
console.log('hi, cardId', cardId);
fetch(`http://localhost:3000/images/${cardId}`)
.then((resp) => resp.json())
.then((resp) => {
this.setState({
image: resp
})
console.log(this.state.image);
})
}
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<Card
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</Card>
);
});
}
}
export default Images;
child component:
i'm not hitting the console.log here, so no more code!
import React from 'react';
import { Card, Image } from 'semantic-ui-react';
class ImageCard extends React.Component {
render() {
console.log('image card');
return (
<Card>
</Card>
);
}
}
export default ImageCard;
I left a comment with a few improvements to the code you could make. Specifically:
You have an extra </div> in your ImagesContainer.
Also, you'll want to remove onClick={this.cardClickHandler} from ImagesContainer as cardClickHandler is defined not on ImagesContainer but instead on your Images component.
But the problem is that you are not rendering your ImageCard component at all. You are just rendering <Card> instead of <ImageCard>
Specifically, your parent component's render should change from this:
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<Card
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</Card>
);
});
}
to this:
render() {
const allImages = this.props.images;
return allImages.map((image) => {
return (
<ImageCard
key={image.id}
className="photo"
data-id={image.id}
data-name={image.name}
onClick={this.cardClickHandler}
>
<img
src={image.image}
alt=""
data-id={image.id}
data-name={image.name}
className="photo-image"
height={265}
/>
</ImageCard>
);
});
}

Re-render a component on repeat click on the same tab in another component reactjs

I know the title might be confusing, as well as might sound as a repeat, please read the whole description before marking it as repeat, I am new to react and need some help.
I am building a dashboard. I have a navigation bar div which has multiple tabs and a content div which has the corresponding content. Once a tab is clicked i render its corresponding content. Within any tab the user can do various things/changes. Lets say i have a tab ABC which when clicked produces an initial view, when i click this tab again when it is already clicked i need to re-render the ABC tabs content.
Essentially what i want to do is when after clicking test and test-demo once, user clicks test again the text 'test-demo' should disappear.
import React, { Component } from 'react';
const Button = (props) => {
return (
<button onClick={() => props.onClick(props.buttonName.trim())}>{props.buttonName}</button>
);
};
class Test extends Component {
static initialState = () => ({
appContent:null,
});
state = Test.initialState();
switchTab = (buttonKey) => {
this.setState(prevState => ({
appContent:<a>{buttonKey}</a>
}));
}
render() {
let appContent = null;
switch(this.props.navigationTab) {
case "test":
appContent = <Button onClick={this.switchTab} buttonName='test-demo' />
break;
default:
appContent = null
break;
};
return (
<div>
{appContent}
{this.state.appContent}
</div>
);
}
}
class AppContent extends Component {
render() {
return (
<div>
<Test navigationTab={this.props.navigationTab}/>
</div>
);
}
}
class App extends Component {
static initialState = () => ({
navigationTab:null,
});
state = App.initialState();
switchTab = (buttonKey) => {
this.setState(prevState => ({
navigationTab:buttonKey,
}));
}
render() {
return (
<div>
<div>
<Button onClick={this.switchTab} buttonName='test'/>
</div>
<AppContent navigationTab={this.state.navigationTab} />
</div>
);
}
}
export default App;
https://stackblitz.com/edit/react-fs8u7o?embed=1&file=index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import Hello from './Hello';
import './style.css';
const Button = (props) => {
return (
<button onClick={() => props.onClick(props.buttonName.trim())}>{props.buttonName}</button>
);
};
class Test extends Component {
constructor(props) {
super(props);
this.state = {
appContent: null,
hideTestDemo:false,
};
}
componentWillReceiveProps(nextProps){
this.setState(prevState => ({
hideTestDemo:nextProps.hideTestDemo,
}));
}
switchTab = (buttonKey) => {
this.setState(prevState => ({
appContent: <a>{buttonKey}</a>,
hideTestDemo:false,
}));
}
render() {
let appContent = null;
switch (this.props.navigationTab) {
case "test":
appContent = <Button onClick={this.switchTab} buttonName='test-demo' />
break;
default:
appContent = null
break;
};
return (
<div>
{appContent}
{(!this.state.hideTestDemo ) ? this.state.appContent:null}
</div>
);
}
}
class AppContent extends Component {
render() {
return (
<div>
<Test {...this.props} />
</div>
);
}
}
class App extends Component {
constructor() {
super();
this.state = {
navigationTab: null,
};
}
hideTestDemo = false;
switchTab = (buttonKey) => {
if (this.hideTestDemo)
this.setState(prevState => ({
navigationTab: buttonKey,
hideTestDemo: true,
}));
else
this.setState(prevState => ({
navigationTab: buttonKey,
hideTestDemo:false,
}));
this.hideTestDemo=!this.hideTestDemo;
}
render() {
return (
<div>
<div>
<Button onClick={this.switchTab} buttonName='test' />
</div>
<AppContent {...this.state} />
</div>
);
}
}
render(<App />, document.getElementById('root'));

Resources