Reactjs context API rerender all the components which consumes Context API - javascript

I am new to Reactjs and building a sample e commerce application in Reactjs. I am using single context API to share multiple filter values across application. When I update values from any of the filter components, all the filter components get re rendered again. Is there any way to restrict this behavior as it causes performance issues. Sharing the sample code link below.
stackblitz
App component
import logo from './logo.svg';
import React, { useState } from 'react';
import './App.css';
import Categories from './components/Categories/Categories';
import Filters from './components/Filters/Filters';
import Products from './components/Products/Products';
import styled from 'styled-components';
import { FilterContext } from './components/Contexts/FilterContext';
const MainContainer = styled.div`
display: flex;
padding: 20px;
`
function App() {
console.log('App component');
const [rangeval, setRangeval] = useState(null);
const [colorCodes, setcolorCodes] = useState([]);
return (
<div className="App">
<Categories/>
<FilterContext.Provider value={React.useMemo(()=>({slider: {rangeval, setRangeval}, color: {colorCodes, setcolorCodes} }), rangeval, setRangeval, colorCodes, setcolorCodes)} >
<MainContainer>
<Filters/>
<Products />
</MainContainer>
</FilterContext.Provider>
</div>
);
}
export default React.memo(App);
Below are my filter components.
Color component
import axios from 'axios';
import React, { useCallback, useContext, useEffect, useState } from 'react';
import styled from 'styled-components';
import { FilterContext } from '../../Contexts/FilterContext';
const ColorWrapper = styled.div`
display: flex;
flex-direction: column;
padding: 20px 10px 20px 10px;
border-bottom: 1px solid grey;
`;
function Color() {
console.log('Color Component');
const { color } = useContext(FilterContext);
const [colors, setColors] = useState([]);
const colorCodes = [];
const handleCheckboxState = (data) => {
if (data.isChecked) {
colorCodes = colorCodes.filter((item) => item != data.id);
} else {
colorCodes.push(data.id);
}
colors.find((item) => item.id === data.id).isChecked = !data.isChecked;
setColors(colors);
color.setcolorCodes(colorCodes);
};
useEffect(() => {
axios
.get('https://run.mocky.io/v3/fdbe3884-b824-466c-8da4-0d0ecad17e7c')
.then((response) => {
setColors(response.data);
})
.catch((error) => {})
.finally();
}, []);
return (
<ColorWrapper>
<div>Color</div>
{colors.map((color) => (
<label key={color.id}>
<input
type="checkbox"
checked={color.isChecked}
name="color"
value={color.id}
onChange={() => handleCheckboxState(color)}
/>
{color.name}
</label>
))}
</ColorWrapper>
);
}
export default React.memo(Color);
Slider component
import React, { useContext, useState } from 'react';
import { FilterContext } from '../../Contexts/FilterContext';
import styled from 'styled-components';
const SliderWrapper = styled.div`
display: flex;
flex-direction: column;
padding: 20px 10px 20px 10px;
border-bottom: 1px solid grey;
`;
const SliderInput = styled.input`
width: 100%;
`;
const SliderRange = styled.div`
margin: 20px 0px 20px 0px;
`;
function Slider() {
console.log('Slider component');
const { slider } = useContext(FilterContext);
const [min, setMin] = useState(10);
const [max, setMax] = useState(10000);
return (
<SliderWrapper>
<SliderRange>
Price Range 0 - {slider.rangeval ? slider.rangeval : min}
</SliderRange>
<SliderInput
type="range"
className="range"
min={min}
max={max}
onChange={(event) => slider.setRangeval(event.target.value)}
/>
</SliderWrapper>
);
}
export default React.memo(Slider);

Related

how to type props for text area react typescript

I have a react component that looks like this:
import { TextareaHTMLAttributes} from 'react'
import styled from 'styled-components'
const TextAreaElement = styled.textarea`
border-radius: 40px;
border: none;
background: white;
`
const TextArea = (props: TextareaHTMLAttributes<any>) => { <--- replace <any> here
return <TextAreaElement {...props} />
}
I know I can do something like this, but would rather not have to add every prop manually:
const TextArea = ({placeholder} : {placeholder: string}) => {
return <TextAreaElement placeholder={placeholder} />
}
You can pass the props as regular HTML element
import React from "react";
const CustomTA = (props: React.HTMLProps<HTMLTextAreaElement>) => {
return <textarea {...props} />;
};

React native styled components cannot override component's style

I have a weird problem with stlyed components. I have a component Header with a basic style but when a try to use this component and extend the style nothing happens. Can someone tell me what going on?
import styled from 'styled-components/native';
export const Container = styled.SafeAreaView``;
export const Content = styled.View`
height: 72px;
padding: 0 24px;
flex-direction: row;
align-items: center;
justify-content: center;
`;
Header component
import React, { PropsWithChildren, FC } from 'react';
import { Container, Content } from './styles';
const Header: FC = ({ children }: PropsWithChildren<unknown>, props) => {
return (
<Container {...props}>
<Content>{children}</Content>
</Container>
);
};
export default Header;
import styled from 'styled-components/native';
import Header from '../components/Header/index';
export const Container = styled(Header)`
background: blue;
height: 200px;
`;
You have to pass your props from into your Header component. In Container or Content. It's won't be done instead of you.
Your Header is a React component and he "doesn't know what to do" with props that it will receive from Container - const Container = styled(Header)'...'.
Props will be recognized correctly if component is working with styles, as Text, View, ...
export const Container = styled(Header)`
background: blue;
height: 200px;
`;
const Header: FC = ({ children, ...restProps }: PropsWithChildren<unknown>) => {
return (
<Container {...restProps}>
<Content>{children}</Content> // or <Content {...restProps}>...
</Container>
);
};
or you have 2 next options, without passing the props - just editing your inner Container. It's depends on your codestyle of the project
const Header: FC = ({ children }: PropsWithChildren<unknown>) => {
return (
<Container background="blue" height="200px">
<Content>{children}</Content>
</Container>
);
};
export const NewContainer = styled(Container)`
background: blue;
height: 200px;
`;
const Header: FC = ({ children }: PropsWithChildren<unknown>) => {
return (
<NewContainer>
<Content>{children}</Content>
</NewContainer>
);
};

How to use const of one component in another component

I'm new to reactjs. I'm trying to implement a toggleTheme action in every page of my project.
So, Instead of declaring it in every component, I thought of creating a toggle theme component itself and import it in all other components.
I don't know why but for some reason it is not working properly.
Here is my code...
toggletheme.js
import Brightness4Icon from '#material-ui/icons/Brightness4';
import Brightness7Icon from '#material-ui/icons/Brightness7';
import React, { useState } from 'react';
import './BasePage.css';
const ToggleTheme = (isLight, setLight) => {
[isLight, setLight] = useState("true");
const toggleTheme = () => {
setLight(!isLight);
}
console.log(isLight)
return (
<div className="themebtn" onClick={toggleTheme}>
{isLight ? <Brightness7Icon /> : <Brightness4Icon />}
</div>
)
}
export default ToggleTheme
Another component in which I want to import toggletheme component basepage.js
import ToggleTheme from './ToggleTheme'
import React, { useState } from 'react';
const Basepage = () => {
return (
<div className={isLight ? "light" : "dark"}>
<div>
<ToggleTheme />
</div>
</div>
)
}
export default Basepage
basepage.css
.light {
--background: #ffffff;
--background-color: #f1f1f1;
--blue: #1b98f5;
--foreground: #323234;
--shadow: 4px 4px 4px #aaa;
}
.dark {
--background: #323234;
--background-color: #202124;
--blue: #1b98f5;
--foreground: #f1f1f1;
--shadow: 4px 4px 4px #222;
}
I'm getting at isLight in my basepage.js
I would appreciate some help in rectifying it.
Thank you.
It is very simple, because you are using a string "true" to set the initial state of the variable in your file toggletheme.js and remember that a string with characters is always true, that is why in the ternary operator you asked if the variable was true or not, and it returns always true.
Just change this useState("true") to useState(true).
import Brightness4Icon from '#material-ui/icons/Brightness4';
import Brightness7Icon from '#material-ui/icons/Brightness7';
import React, { useState } from 'react';
import './BasePage.css';
const ToggleTheme = (isLight, setLight) => {
[isLight, setLight] = useState(true);
const toggleTheme = () => {
setLight(!isLight);
}
console.log(isLight)
return (
<div className="themebtn" onClick={toggleTheme}>
{isLight ? <Brightness7Icon /> : <Brightness4Icon />}
</div>
)
}
export default ToggleTheme
const ToggleTheme = ({isLight, setLight}) => {
const toggleTheme = () => {
setLight(!isLight);
}
console.log(isLight)
return (
<div className="themebtn" onClick={toggleTheme}>
{isLight ? <Brightness7Icon /> : <Brightness4Icon />}
</div>
)
}
const Basepage = () => {
const [isLight, setLight] = React.useState(true);
return (
<div className={isLight ? "light" : "dark"}>
<div>
<ToggleTheme isLight={isLight} setLight={setLight} />
</div>
</div>
)
}
I have majorly used chakra-ui and I have hated the fact that you can use true and false to set the theme, what if you choose to have a different theme down the road ?
import Brightness4Icon from '#material-ui/icons/Brightness4';
import Brightness7Icon from '#material-ui/icons/Brightness7';
import { useEffect, useState } from 'react';
const useTheme = () => {
let themes=['light','dark'];
let icons =[ <Brightness7Icon /> , <Brightness4Icon />]
const [icon,setIcon]=useState(<Brightness7Icon />);
let [theme, setTheme] = useState("light");
let changeTheme=()=>{
let index =themes.indexOf(theme)
if(index==themes.length-1)
{
setTheme(themes[0]);
setIcon(icons[0]);
}
else{
setTheme(themes[index+1]);
setIcon(icons[index+1]);
}
}
useEffect(()=>{
},[theme])
return ([theme,changeTheme,<div onClick={changeTheme}>{icon}</div>])
}
export default useTheme
import useTheme from './toggle'
import React from 'react';
const Basepage = () => {
let [theme,changeTheme,icon] = useTheme();
return (
<div className={theme}>
<div>
{icon}
</div>
</div>
)
}
export default Basepage

Objects are not valid as a React child (found: object with keys {content}). If you meant to render a collection of children, use an array instead

I am building a todo website using react and firebase But i am getting an Error which is given below
Error: Objects are not valid as a React child (found: object with keys {content}). If you meant to render a collection of children, use an array instead.
All the components are given below
App.js
import './App.css';
import {useEffect, useState} from 'react'
import Todo from './Todo'
import FormControl from '#material-ui/core/FormControl';
import { Button, Input, InputLabel } from '#material-ui/core';
import db from './firebase';
import firebase from 'firebase'
function App() {
const [inputcontent , setInputcontent] = useState("");
const [inputtitle , setInputtitle] = useState("");
const [todos , setTodos] = useState([]);
useEffect(() => {
db.collection('todos').orderBy('timestamp' , 'desc').onSnapshot(snapshot =>{
setTodos(snapshot.docs.map(doc => ({id : doc.id , todo : doc.data()})));
})
} , []);
const addTodo = (event) =>{
event.preventDefault();
db.collection("todos").add({
title : inputtitle,
content : inputcontent,
timestamp : firebase.firestore.FieldValue.serverTimestamp()
});
setInputcontent("");
setInputtitle("");
}
return (
<div className="App">
<h1>Todos List</h1>
<form className = "form">
<FormControl>
<InputLabel>Enter Title</InputLabel>
<Input className = "form__inside" type="text" value ={inputtitle} onChange = {(event) => setInputtitle(event.target.value)} />
</FormControl>
<FormControl>
<InputLabel>Enter Todo</InputLabel>
<Input className = "form__inside" type="text" value ={inputcontent} onChange = {(event) => setInputcontent(event.target.value)} />
</FormControl>
<Button className = "form__inside" type = "submit" onClick = {addTodo} disabled ={!inputtitle || !inputcontent} variant="contained" color="secondary">Add Todo</Button>
</form>
{
todos.map(({id, todo}) =>(
<Todo key = {id} id = {id} title = {todo.title} content = {todo.content}/>
))
}
</div>
);
}
export default App;
App.css
* {
margin: 0px;
}
.App {
text-align: center;
background-color: rgba(211, 211, 211, 0.692);
}
.form {
display: flex;
flex-direction: column;
margin-left: auto;
margin-right: auto;
justify-content: center;
margin-top: 30px;
margin-left: 30px;
margin-right: 30px;
}
.form__inside {
margin-bottom: 10px;
}
Todo.js
import React from 'react'
import { makeStyles } from '#material-ui/core/styles';
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import Typography from '#material-ui/core/Typography';
import './Todo.css'
import db from './firebase';
import { Button } from '#material-ui/core';
const useStyles = makeStyles({
title: {
fontSize: 20,
fontWeight: 550,
},
paragraph: {
fontSize : 20,
}
});
function Todo({id , title , content}) {
const classes = useStyles();
return (
<div className = "each__todo">
<Card>
<CardContent>
<Typography className={classes.title} >
{title}
</Typography>
<Typography className={classes.paragraph}>
{content}
</Typography>
</CardContent>
</Card>
<Button onClick = {event =>{ db.collection('todos').doc(id).delete()}}>Delete</Button>
</div>
)
}
export default Todo
Todo.css
.each__todo {
padding: 10px;
margin: 10px;
width: fit-content;
}
firebase.js
import firebase from "firebase";
const firebaseApp = firebase.initializeApp({
apiKey: "AIzaSyBS2UzzZAx1fOnensgiOy9YnYbOZ8CzE4A",
authDomain: "todo-clone-b004b.firebaseapp.com",
projectId: "todo-clone-b004b",
storageBucket: "todo-clone-b004b.appspot.com",
messagingSenderId: "775676178310",
appId: "1:775676178310:web:23d69a5fb4db112fadc607",
measurementId: "G-NNW9560NY7"
});
const db = firebaseApp.firestore();
export default db ;
Please help
Thank you

State does not change via redux when default props is assigned

I'm using redux in my react project. I set initial state in my reducer
const initialState = {
isWarning: false,
};
and I have default props in my react component
LoginView.defaultProps = {
warning: false,
};
and of course I'm destructuring my props in render method
const { warning } = this.props;
here you have code from my component
import React, { Component } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import axios from '../../axios';
import Input from '../Input/Input';
import LoggedIn from '../LoggedIn/LoggedIn';
import * as actions from '../../store/actions';
const StyledWrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
`;
const StyledTitle = styled.p`
color: #727272;
margin: 89px 0 73px 0;
font-size: 24px;
`;
const StyledButton = styled.button`
border: none;
border-radius: 6px;
background: #1de278;
height: 48px;
width: 397px;
color: #fff;
font-size: 18px;
cursor: pointer;
&:active,
&:focus {
outline: none;
}
`;
class LoginView extends Component {
state = {
email: '',
password: '',
};
onType = event => {
this.setState({ [event.target.id]: event.target.value });
};
onSubmit = (email, password) => {
axios
.post('api/v1/session', {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
email,
password,
})
// eslint-disable-next-line react/destructuring-assignment
.then(() => this.props.history.push('/'))
.catch(() => this.props.onLoginError(true));
};
render() {
const { email, password } = this.state;
const { warning } = this.props;
return (
<StyledWrapper>
{console.log(warning)}
<StyledTitle>Log in</StyledTitle>
<Input id="email" placeholderText="Email address"
setInputValue={this.onType} />
<Input
id="password"
placeholderText="Password"
setInputValue={this.onType}
type="password"
/>
<LoggedIn />
<StyledButton onClick={() => this.onSubmit(email,
password)}>Login</StyledButton>
</StyledWrapper>
);
}
}
const mapStateToProps = state => {
return { isWarning: state.login.isWarning };
};
const mapDispatchToProps = dispatch => {
return {
onLoginError: state => dispatch(actions.setErrorLogin(state)),
};
};
LoginView.propTypes = {
warning: PropTypes.bool,
};
LoginView.defaultProps = {
warning: false,
};
export default connect(
mapStateToProps,
mapDispatchToProps,
)(LoginView);
Redux dev tools shows my updated state from false to true, but my warning have false value. In my opinion it's something with default props. Do you have any ideas what could be a problem?
The problem is here:
const mapStateToProps = state => {
return { isWarning: state.login.isWarning };
};
it's supposed to be
const mapStateToProps = state => {
return { warning: state.login.isWarning };
};
Because the component can't find a warning prop it sets it to the default value, so you have to give it the same name.

Categories

Resources