I was having a parent component named Cart.jsx in react and the child component named Card.jsx.
The Parent component looks like this.
Cart.jsx
import React, { useState, useEffect, useContext } from "react";
import Card from "../../Components/Card";
function Cart() {
const cart = [**Array of Objects**]
const [total,setTotal] = useState([]);
return (
<div className="cart__Items">
<Card item={cart[0]} />;
<Card item={cart[1]} />;
<Card item={cart[2]} />;
</div>
)
}
export default Cart;
And the Card Component looks as follows
Card.jsx
import React, { useState } from "react";
function Card() {
const [price,setPrice] = useState(0);
// in-between implemented some function to calculate price value.
return (
<div>
// rendering code
</div>
)
}
export default Card;
Now the problem is, how do I get the price data of each child component and store them in the total array of the parent component.
Here the parent has 3 Cardcomponents, I need to get the price data from each component and store them in the Cart component
Here is the code. I hope this might help
import React, { useState, useEffect, useContext } from "react";
import Card from "../../Components/Card";
function Cart() {
const cart = [**Array of Objects**]
const [total,setTotal] = useState(0);
return (
<div className="cart__Items">
{cart.map(crt =><Card item={crt} total={total} setTotal={setTotal} />}
</div>
)
}
export default Cart;
import React, { useState } from "react";
function Card(props) {
const [price,setPrice] = useState(0);
const {setTotal, total} = props
useEffect(()=>{
setTotal(total+price)
},[])
// in-between implemented some function to calculate price value.
return (
<div>
// rendering code
</div>
)
}
export default Card;
import React, { useState, useEffect, useContext } from "react";
import Card from "../../Components/Card";
function Cart() {
const cart = [**Array of Objects**]
const [total,setTotal] = useState([]);
return (
<div className="cart__Items">
{cart.map((item) => (<Card item={item} setTotal={setTotal} />))}
</div>
)
}
export default Cart;```
Now you have access to the setTotal function inside each card Item from which you can update the parent state of "total".
If you feel like the individual prices are to be calculated by the child you should use an event handler prop :
Cart.jsx
import React, { useState, useEffect, useContext } from "react";
import Card from "../../Components/Card";
function Cart() {
const cart = [**Array of Objects**]
const [total,setTotal] = useState(0);
const handlePriceCalculated = price => {
setTotal(total + price);
}
return (
<div className="cart__Items">
<Card item={cart[0]} onPriceCalculated={handlePriceCalculated} />
<Card item={cart[1]} onPriceCalculated={handlePriceCalculated} />
<Card item={cart[2]} onPriceCalculated={handlePriceCalculated} />
</div>
)
}
export default Cart;
Card.jsx
import React, { useState } from "react";
function Card({
onPriceCalculated
}) {
const [price,setPrice] = useState(0);
// in-between implemented some function to calculate price value.
...
setPrice(calculatedValue)
onPriceCalculated(calculatedValue)
...
return (
<div>
// rendering code
</div>
)
}
export default Card;
Giving the responsability to the child to set the total is a bad practise and will result in your components not to be reusable as they would be too hardly coupled.
Related
I can't change the value of the state of the store using the reducer. I'm making an app which has a login-functionality. When a person opens up the app, depending on the fact if he is logged in or not, it should show the right screen. The problem I'm having right now is it doesn't seem to be able to change the store state out of another screen. Anybody who can help me?
import {createStore} from "redux";
const initialState = {
value: false
}
function reducer(state= initialState, action) {
const newState = {...state};
if(action.type === 'login') {
console.log("hahaha you logged in");
newState.value = true;
}
else if(action.type ==='logout') {
console.log("hahaha you logged out")
newState.value = false;
}
return newState;
}
const store = createStore(reducer);
export default store;
This is the store, this should change the value accordingly.
When the login button is pressed on loginscreen it should call the reducer function.
import React, { useRef, useState } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, Image, Dimensions, AsyncStorage } from 'react-native';
import axios from "axios";
import store from "../routes/store"
function LoginScreen({navigation}, props) {
const win = Dimensions.get('window');
const [email,setEmail] = useState('');
const [password, setPassword] = useState('');
const { auth, setAuth } = useAuth();
const [errMsg, setErrMsg] = useState('');
const logInCheck = async (e) => {
console.log("Ingelogd");
store.dispatch({type: 'login'})
}
return(
<Root>
<View style={styles.container}>
<TouchableOpacity style={styles.loginBtn} onPress{logInCheck}>
<Text style={styles.loginText}>LOGIN</Text>
</TouchableOpacity>
</View>
</Root>
)
}
This is the code which should render the right screen depending on the fact if the person is logged in!
import React, { useState, useReducer } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState()? (
<AuthStack.Screen name="Tabs" component={Tabs} options={{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
store.subscribe(rendFunc);
};
export default AuthStack;
The problem with your code is in react re-render rather than the redux store not updating. The store is updating properly but your react component is not aware of any change that has occurred in the store so no re-render is happening.
Firstly you need to add subscriptions to the redux store listener in useEffect i.e. when the component is mounted and later unsubscribe to prevent memory leakages. The redux subscribe function takes in a function to handle whenever state change has occurred in the redux store.
In this function, you can create a state using useState to create a re-render of the component.
use the below code in the authstack and it should work fine.
if any more queries you can contact me # wizcoderzcorp#gmail.com
import React, { useState, useReducer, useEffect } from "react";
import { createStore } from 'redux';
import { View,Text } from "react-native";
import { createNativeStackNavigator } from '#react-navigation/native-
stack';
import LoginScreen from "../screens/LoginScreen";
import * as SecureStore from 'expo-secure-store';
import axios from "axios";
import Tabs from "./Tabs";
import store from "./store";
import ForgotPasswordScreen from "../screens/ForgotPassword";
const AuthStack = () => {
const [loginState, setLoginState] = useState(false)
const handleReduxStateChange = () =>{
setLoginState(store.getState().value)
}
useEffect(()=>{
const unsubscribe = store.subscribe(handleReduxStateChange);
return unsubscribe()
},[])
function rendFunc() {
console.log(store.getState());
return(
<AuthStack.Navigator>
{!store.getState().value? (
<AuthStack.Screen name="Tabs" component={Tabs} options=
{{headerShown : false}}/>
) : (
<>
<AuthStack.Screen
name = "LoginScreen"
component={LoginScreen}
/>
<AuthStack.Screen
name = "ForgotPassword"
component={ForgotPasswordScreen},
/>
</>
)
}
</AuthStack.Navigator>
);
}
return (
rendFunc()
);
};
export default AuthStack;
In my app, I want to pass the global store of my application to the child components via context. For the sake of an example, I have created 2 child components namely Child1 and Child2, and passing increment and decrement counter functions along with the corresponding counter values to them. Child1 is responsible for incrementing counter1 and child2 for decrementing counter2. When I am invoking the increment/decrement function in the components, the other component is uselessly getting re-rendered along with the parent. How can I prevent this from happening?
Please find the above use-case here
Below is the code for the same,
App.js
import React, { useState, useCallback } from 'react';
import './style.css';
import { UserContext } from './Context.js';
import Child1 from './Child1';
import Child2 from './Child2';
export default function App() {
const [counter1, setCounter1] = useState(0);
const [counter2, setCounter2] = useState(0);
const IncrementCounter = useCallback(
() => setCounter1((prevState) => prevState + 1),
[setCounter1]
);
const DecrementCounter = useCallback(
() => setCounter2((prevState) => prevState - 1),
[setCounter2]
);
const store = {
child1: {
counter1,
IncrementCounter,
},
child2: {
counter2,
DecrementCounter,
},
};
console.log('App re-rendering');
return (
<UserContext.Provider value={store}>
<Child1 />
<Child2 />
</UserContext.Provider>
);
}
Child1.js
import React, { useContext } from 'react';
import { UserContext } from './Context.js';
const Child1 = () => {
const store = useContext(UserContext);
const { child1 } = store;
console.log('Child1 Re-rendering');
return (
<div>
<p>{`Counter1 value : ${child1.counter1}`}</p>
<button onClick={child1.IncrementCounter}>Increment</button>
</div>
);
};
export default React.memo(Child1);
Child2.js
import React, { useContext } from 'react';
import { UserContext } from './Context.js';
const Child2 = () => {
const store = useContext(UserContext);
const { child2 } = store;
console.log('Child2 Re-rendering');
return (
<div>
<p>{`Counter2 value : ${child2.counter2}`}</p>
<button onClick={child2.DecrementCounter}>Decrement</button>
</div>
);
};
export default React.memo(Child2);
Context.js
import React from 'react';
const UserContext = React.createContext();
export { UserContext };
Consider the code :
APP.JS
import React, { useState, useMemo } from 'react';
import Counter from './components/Counter';
import './App.css';
function App() {
const [countA, setCountA] = useState(0);
const incrementA = () => {
setCountA(countA + 1);
};
// const memoCounter = useMemo(() => {
// return <Counter />;
// }, []);
return (
<div className='App'>
<h1>Incrementing CountA from APP.JS : {countA}</h1>
<p>
<button onClick={incrementA}>Increment A</button>
</p>
{/* {memoCounter} */}
<Counter />
</div>
);
}
export default App;
Counter.js :
import React, { useEffect } from 'react';
let renderCount = 1;
const Counter = () => {
useEffect(() => {
renderCount++;
});
return (
<div>
<h1>Rendering Counter component : {renderCount}</h1>
</div>
);
};
export default Counter;
When the user hits the button and increments , React renders Counter component all over again , even though I don't pass anything to it.
However when I put useMemo it doesn't.
Why ?
By default when a parent component renders (App), it renders all its children (Counter).
To overload the default behaviour, use React API like React.memo:
const Counter = () => {...}
export default React.memo(Counter);
I am trying to use the React useState Hook for an online project. What I want to happen is, when I type the users name in the search box, It will find the users card on the browser. I am able to log the user to the console, but I am stuck on how to get it to render on screen. Tried so many ways and just not getting it.
console output
App.js
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput] = useState('');
function onSearchChange(e) {
searchInput = e.target.value;
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
console.log(filteredRobots);
}
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList id={robots.id} name={robots.name} email={robots.email}/>
</div>
);
}
export default App;
CardList.js
import React from 'react';
import Card from './Card';
import {robots} from './robots';
function CardList(props) {
return (
<div>
{
robots.map(function(user) {
return <Card key={user.id} id={user.id} name={user.name} email={user.email} />
})
};
</div> )
}
export default CardList;
Card.js
import React from 'react';
import 'tachyons';
function Card(props) {
return (
<div className='bg-light-green dib br3 pa3 ma2 grow shadow-5'>
<img src={`https://robohash.org/${props.id}`} alt="Robot" />
<h2>{props.name}</h2>
<p>{props.email}</p>
</div>
);
}
export default Card;
React only re-render when you set a state to a new value.
Check the code below:
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSeachInput] = useState('');
function onSearchChange(e) {
// set state here to re-render
setSeachInput(e.target.value);
}
// use might want to use useMemo to improve this, I just want to make it simple now
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
console.log(filteredRobots);
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
{/* using filteredRobots herer*/}
<CardList id={filteredRobots.id} name={filteredRobots.name} email={filteredRobots.email}/>
</div>
);
}
export default App;
In your App.js file, the searchInput is not being set to the state
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSearchInput] = useState('');
function onSearchChange(e) {
setSearchInput(e.target.value)
}
**You can pass the filterRobots in place of robots to get only words passed in the search box**
const filteredRobots = robots.filter(function(robot){
return robot.name.toLowerCase().includes(searchInput.toLowerCase());
});
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList robots={filteredRobots}/>
</div>
);
}
export default App;
In the CardList File
import React from 'react';
import Card from './Card';
function CardList({robots}) {
{
robots.map((user, i) => {
return (
<Card
key={i}
id={user[i].id}
name={user[i].name}
email={user[i].email}
/>
);
})
}
}
export default CardList;
You should not be mutating the searchInput value like searchInput = e.target.value. It is better to call a setter function to update the value. For example,
const [searchInput, setSearchInput] = useState('');
// to update the value of searchInput call setSearchInput
function onSearchChange(e) {
setSearchInput(e.target.value)
}
State changes are asynchronous. When you try to filter the robots it is not guaranteed that it will be called with the latest value of searchInput that's why you should be using useEffect hook which will filter the robots when the value of searchInput changes.
Here is a solution,
import React, { useState } from 'react';
import CardList from './CardList';
import {robots} from './robots';
import SearchBox from './SearchBox';
function App() {
let [searchInput, setSearchInput] = useState('');
let [filterdRobots, setFilteredRobots] = useState(robots);
function onSearchChange(e) {
setSearchInput(e.target.value);
}
useEffect(() => {
setFilteredRobots(robots.filter(r =>
r.name.toLowerCase().includes(searchInput.toLowerCase())))
}, [searchInput, robots])
return (
<div className='tc'>
<h1>RoboFriends</h1>
<SearchBox searchChange={onSearchChange} />
<CardList robots={filteredRobots}/>
</div>
);
}
export default App;
check the codesanbox for demo
I have an application in reactjs. I want to get data from the input, from Component.js, into Component2.js. The function that get data from input is stored in sources.js.
export const getData = e => {
const value = e.target.value;
console.log(value);
return value;
};
Unfortunately when i want to get data in Component2, i don't get anything.
Question: How keeping this structure of folders to get data from Component1 in Component2?
Demo https://codesandbox.io/s/vigorous-vaughan-62mqe?file=/src/Component2.js
You can use React Context API to achieve this solution.
you can lifted up your state from child to parent and then pass that data from parent to another child (sibling). Here is the example:
Parent Component
import React, {useEffect, useState} from 'react';
import Child from "./Child";
import Sibling from "./Sibling";
function CParent(props) {
const [value, setValue] = useState(false);
function setOpeningValue(value) {
console.log('From Child to Parent:' + value);
setValue(value);
}
return (
<div>
<Child setOpeningValue={setOpeningValue}/>
<Sibling value={value}/>
</div>
);
}
export default CParent;
Child Component
import React, {useEffect, useState} from 'react';
// Child to Parent communication
function Child(props) {
const {setOpeningValue} = props;
const [value, setValue] = useState('');
function clickHandler() {
setOpeningValue(`changes is ${value}`);
}
function changeHandler(event) {
setValue(event.target.value);
}
return (
<div>
<input onChange={changeHandler} />
<button onClick={clickHandler}>pass data to parent</button>
</div>
);
}
export default Child;
Sibling Component
import React, {useEffect, useState} from 'react';
function Sibling(props) {
const {value} = props;
return (
<div>
This value is showing from Child to Parent to Another Child (Sibling): {value}
</div>
);
}
export default Sibling;