After showing the content for searched item, while removing the letters from search bar not showing the contents correctly. How to show the contents based on the word which is there in search bar. I have started to learn redux. So need some suggestions
import logo from "./logo.svg";
import "./App.css";
import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
function App() {
const [name, setName] = useState("");
const [searchTerm, setSearchterm] = useState("");
const dispatch = useDispatch();
const data = useSelector((state) => state.add);
console.log(data, "dfata");
const handleChange = (e) => {
setName(e.target.value);
};
console.log(name);
if (data.length == 0) {
return <p>No data</p>;
}
const Submithandler = () => {
dispatch({ type: "ADD_ITEM", name });
setName("");
};
const handleSearch = (e) => {
setSearchterm(e.target.value);
};
const submitSerach = () => {
dispatch({ type: "SEARCH_ITEM", searchTerm });
};
const reset = () => {
dispatch({ type: "RESET", searchTerm });
};
return (
<div className="App">
{data.loading && <p>loading</p>}
<input value={searchTerm} onChange={(e) => handleSearch(e)} />
<button onClick={() => submitSerach()}>search</button>
<button onClick={() => reset()}>reset</button>
<input value={name} onChange={handleChange} />
<button onClick={Submithandler}>Add</button>
{data.item.length === 0 && <p>no item</p>}
{data.item.map((dta, i) => {
return (
<div>
{dta}
<button
onClick={() => dispatch({ type: "REMOVE_ITEM", name: dta })}
>
Remove
</button>
</div>
);
})}
</div>
);
}
export default App;
const INITIAL_STATE = {
item: [],
loading: false,
};
function addReducer(state = INITIAL_STATE, action) {
switch (action.type) {
case "ADD_ITEM":
console.log(action, "ahghsgda");
return { item: [...state.item, action.name] };
case "REMOVE_ITEM":
console.log(action, "REMOPVE");
return {
item: state.item.filter((inditem) => inditem !== action.name),
};
case "SEARCH_ITEM":
console.log(action, "ahghsgda");
const data = [...state.item];
return {
loading: true,
item: [data.filter((product) => product.includes(action.searchTerm))],
};
case "RESET":
return {
item: [...state.item],
};
default:
return state;
}
}
export default addReducer;
After showing the content for searched item, while removing the letters from search bar not showing the contents correctly
Related
I am dispatching an action that is supposed to take an input from the user and store it in a database. However, when I inspect my posts state in redux after the action is dispatched, there is a null value appended to the state array before the actual post. This is preventing me from working with the actual data in the posts array. Basically I'm wondering how to prevent null from being appended each time I dispatch a new post. Here are the relevant code snippets and images.
Post Reducer:
import { enableAllPlugins, produce } from 'immer';
enableAllPlugins();
const initialState = {
posts: [],
loading: false,
error: false,
uploading: false,
};
const postReducer = produce((draftstate, action = {}) => {
switch (action.type) {
case 'UPLOAD_START':
draftstate.loading = true;
draftstate.error = false;
case 'UPLOAD_SUCCESS':
draftstate.posts.push(action.data);
draftstate.uploading = false;
draftstate.error = false;
case 'UPLOAD_FAIL':
draftstate.uploading = false;
draftstate.error = true;
default:
return draftstate;
}
}, initialState);
export default postReducer;
Upload Post action:
export const uploadPost = (data) => async (dispatch) => {
dispatch({ type: 'UPLOAD_START' });
try {
const newPost = await UploadApi.uploadPost(data);
console.log('new post before', newPost);
dispatch({ type: 'UPLOAD_SUCCESS', data: newPost.data });
} catch (error) {
console.log(error);
dispatch({ type: 'UPLOAD_FAIL' });
}
};
Share Post code:
import React, { useState, useRef } from "react";
import ProfileImage from "../../img/profileImg.jpg";
import "./PostShare.css";
import { UilScenery } from "#iconscout/react-unicons";
import { UilPlayCircle } from "#iconscout/react-unicons";
import { UilLocationPoint } from "#iconscout/react-unicons";
import { UilSchedule } from "#iconscout/react-unicons";
import { UilTimes } from "#iconscout/react-unicons";
import { useSelector, useDispatch } from "react-redux";
import { uploadImage, uploadPost } from "../../actions/uploadAction";
const PostShare = () => {
const loading = useSelector((state) => state.postReducer.uploading);
const [image, setImage] = useState(null);
const imageRef = useRef();
const desc = useRef();
const dispatch = useDispatch();
const { user } = useSelector((state) => state.authReducer.authData);
// handle Image Change
const onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
let img = event.target.files[0];
setImage(img);
}
};
const reset = () => {
setImage(null);
desc.current.value = "";
};
const handleSubmit = async (e) => {
e.preventDefault();
const newPost = {
userId: user._id,
desc: desc.current.value,
};
if (image) {
const data = new FormData();
const filename = Date.now() + image.name;
data.append("name", filename);
data.append("file", image);
newPost.image = filename;
console.log(newPost);
try {
dispatch(uploadImage(data));
} catch (error) {
console.log(error);
}
}
dispatch(uploadPost(newPost));
reset();
};
return (
<div>
<div className="PostShare">
<img src={ProfileImage} alt="" />
<div>
<input
ref={desc}
required
type="text"
placeholder="What's happening"
/>
<div className="postOptions">
<div
className="option"
style={{ color: "var(--photo)" }}
onClick={() => imageRef.current.click()}
>
<UilScenery />
Photo
</div>
<div className="option" style={{ color: "var(--video" }}>
<UilPlayCircle />
Video
</div>
<div className="option" style={{ color: "var(--location)" }}>
<UilLocationPoint />
Location
</div>
<div className="option" style={{ color: "var(--shedule)" }}>
<UilSchedule />
Schedule
</div>
<button
className="button ps-button"
onClick={handleSubmit}
disabled={loading}
>
{loading ? "Uploading..." : "Share"}
</button>
<div style={{ display: "none" }}>
<input
type="file"
name="myImage"
ref={imageRef}
onChange={onImageChange}
/>
</div>
</div>
{image && (
<div className="previewImage">
<UilTimes onClick={() => setImage(null)} />
<img src={URL.createObjectURL(image)} alt="" />
</div>
)}
</div>
</div>
</div>
);
};
export default PostShare;
I would be glad to provide any other details if that helps.
Update with other portions of code:
Dispatcher of RETRIEVING_SUCCESS:
import * as PostApi from '../api/PostRequest';
export const getTimelinePosts = (id) => async (dispatch) => {
dispatch({ type: 'RETRIEVING_START' });
try {
const { data } = await PostApi.getTimelinePosts(id);
dispatch({ type: 'RETRIEVING_SUCCESS', data: data });
} catch (error) {
dispatch({ type: 'RETRIEVING_FAIL' });
console.log(error);
}
};
getTimelinePosts usage:
import React, { useEffect } from 'react';
import './Posts.css';
import { PostsData } from '../../Data/PostsData';
import { useDispatch, useSelector } from 'react-redux';
import { getTimelinePosts } from '../../actions/postAction';
import Post from '../Post/Post';
const Posts = () => {
const dispatch = useDispatch();
const { user } = useSelector((state) => state.authReducer.authData);
let { posts, loading } = useSelector((state) => state.postReducer);
console.log('posts content', posts);
useEffect(() => {
dispatch(getTimelinePosts(user._id));
}, []);
return (
<div className="Posts">
{/* {posts.map((post, id) => {
return <Post data={post} id={id}></Post>;
})} */}
</div>
);
};
export default Posts;
in postReducer, let's remove the default on the switch statement, we don't need it on reducer because other actions will come here and the code make all states return the initial state.
When I try to run this code it gives me this error:
TypeError: Cannot read properties of undefined (reading 'todos')
how to fix it
I wanted to make a todo list that will work after the user registers, but todo does not work after registration
My code:
HomePage.js
const HomePage = () => {
const dispatch = useDispatch();
const { isAuth, email } = useAuth();
return isAuth ? (
<div>
<TodoForm />
<button onClick={() => dispatch(removeUser())}>
Log out from {email}
</button>
</div>
) : (
<Redirect to="/login" />
);
};
TodoForm.js
const TodoForm = () => {
const todos = useSelector((state) => state.todo.todos)
// const todos = useSelector((state) => state.todo.todos);
const dispatch = useDispatch();
const [todoValue, setTodoValue] = useState("");
const addTodoHandler = (e) => {
e.preventDefault();
const todo = {
id: v4(),
text: todoValue,
completed: false,
};
dispatch(addTodo(todo));
setTodoValue("");
};
const handleChange = (e) => {
setTodoValue(e.target.value);
};
console.log(todos);
return (
<>
<form onSubmit={addTodoHandler}>
<input
type="text"
value={todoValue}
onChange={handleChange}
placeholder="Add task"
/>
<button type="submit">Submit</button>
</form>
{todos.map((todo) => (
<TodoList key={todo.id} todo={todo} />
))}
</>
);
};
todoSlice.js
const initialState = {
todos: [],
};
export const todoSlice = createSlice({
name: "todos",
initialState,
reducers: {
addTodo: (state, action) => {
state.todos.push(action.payload);
},
removeTodo: (state, action) => {
state.todos = state.todos.filter((todo) => todo.id !== action.payload);
},
completedTodo: (state, action) => {
const toggleTodo = state.todos.find((todo) => todo.id === action.payload);
toggleTodo.completed = !toggleTodo.completed;
},
},
});
store
reducer: {
todo: todoSlice,
user: userReducer,
},
});
help me fix this, I will be very grateful
slicename is todos not todo
const todos = useSelector((state) => state.todos.todos)
I'm working on a project where I am trying to integrate stripe. I'm trying to get payments to go through with one click of the placeOrderHandler button. Currently, when I click this button I get an error stating: Uncaught (in promise) IntegrationError: Invalid value for stripe.confirmCardPayment intent secret: value should be a client_secret string, and then if I click this button again it goes through and console.log(client_secret) provides the string associated with it. I'm unsure why this is happening, and I would really appreciate any help or advice on how to fix this. Thank you!
PlaceOrderScreen.js
import React, { useState, useEffect} from 'react';
import { useDispatch, useSelector, createSelector } from 'react-redux';
import {createOrder} from '../actions/orderActions';
import {listProducts} from '../actions/productActions';
import { ORDER_CREATE_RESET } from '../constants/orderConstants';
import { PAYMENT_SUCCESS } from '../constants/paymentConstants';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { loadStripe } from "#stripe/stripe-js";
import { Elements } from "#stripe/react-stripe-js";
import {CardElement, PaymentElement, CardNumberElement, CardExpiryElement, CardCvcElement, useStripe, useElements} from '#stripe/react-stripe-js';
import {payment, paymentInfo} from '../actions/paymentActions';
import LoadingSpinner from "../components/LoadingSpinner";
export default function PlaceOrderScreen(props) {
const cart = useSelector((state) => state.cart);
const userSignin = useSelector((state) => state.userSignin);
const { userInfo } = userSignin;
const paymentCreate = useSelector((state) => state.paymentCreate);
const { client_secret } = paymentCreate;
const paymentInformation = useSelector((state) => state.paymentInformation);
const {loadingPayment} = paymentInformation;
const userId = userInfo._id;
const orderCreate = useSelector((state) => state.orderCreate);
const { loading, success, error, order } = orderCreate;
const [loadedPay, setLoadedPay] = useState('');
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { products } = productList;
useEffect(() =>{
dispatch(listProducts({}));
}, [dispatch]);
const elements = useElements();
const stripe = useStripe();
const placeOrderHandler = async (e) => {
e.preventDefault();
if (!stripe || !elements) {
console.log('Stripe.js has not yet loaded.')
return;
}
const paymentMethodType = 'card';
const currency = 'usd';
const {error: backendError} = dispatch(payment(paymentMethodType, currency, cart, userId, ));
if (backendError) {
//addMessage(backendError.message);
console.log(backendError)
return;
}
console.log('Client secret returned')
console.log(client_secret)
const {error: stripeError, paymentIntent} = await stripe.confirmCardPayment(client_secret,
{
payment_method: {
card: elements.getElement(CardNumberElement),
billing_details: {
name: cart.billingAddress.fullName,
},
},
},
)
if (stripeError) {
console.log('stripeError')
return;
}
cart.paymentId = paymentIntent.id
dispatch(createOrder({ ...cart, orderItems: cart.cartItems }))
const orderId = order._id;
setLoadedPay(false)
.then(dispatch(paymentInfo(orderId)))
setLoadedPay(true)
};
const [isLoading, setIsLoading] = useState(false);
var loaded = false;
useEffect(() => {
if (success ) {
props.history.push(`/order/${order._id}`)
dispatch({ type: ORDER_CREATE_RESET })
}
}, [dispatch, order, success]);
return (
<div>
{loadedPay === false ? (<LoadingBox></LoadingBox>) : (
<div>
{loadingPayment & loadingShipping ? (
<LoadingBox></LoadingBox>
) : (
<div>
{isLoading ? <LoadingSpinner /> :
<div>
<div className="row top">
<div className="col-2">
<ul>
<li>
<div className="card card-body">
<h2>Payment</h2>
<div>
<h1>Card</h1>
<form id="payment-form" >
<label htmlFor="card">Card</label>
{/*<CardElement id="card" />*/}
<CardNumberElement id="card"/>
<CardExpiryElement id="card"/>
<CardCvcElement id="card"/>
</form>
</li>
<li>
<button
type="button"
onClick={placeOrderHandler}
disabled={isLoading && cart.cartItems.length === 0}
className="primary block"
>
Place Order
</button>
</li>
{loading && <LoadingBox></LoadingBox>}
{error && <MessageBox variant="danger">{error}</MessageBox>}
</ul>
</div>
</div>
</div>
</div>
}
</div>
)}
</div>
)}
</div>
);
}
paymentReducer.js
export const paymentCreateReducer = (state = {client_secret:[]}, action) => {
switch (action.type) {
case PAYMENT_REQUEST:
return { ...state, loading: true };
case PAYMENT_SUCCESS:
return { ...state, loading: false, client_secret: action.payload};
case PAYMENT_FAIL:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
Backend
stripe.js
....
stripeRouter.get(
'/config',
expressAsyncHandler((req, res) => {
res.send({
publishableKey: '',
});
}));
stripeRouter.post(
'/pay',
expressAsyncHandler(async (req, res) => {
const userId = req.body.userId
const user = await User.findById(userId);
stripeClient.paymentIntents.create({
payment_method_types: [req.body.paymentMethodType],
amount: amount,
currency: req.body.currency,
})
.catch(function(err) {
console.log("There was an error"})
.then(async function(paymentIntents) {
const clientSecret = paymentIntents.client_secret
const client_secret = clientSecret.toString()
res.status(201).send(client_secret)
})
}));
In the categories component, I render a random image from each category. I also added a onClick event to each image. When the image is clicked, it will dispatch the action getCategory(target.alt) and the DOM will render the products from the clicked category. The problem I got is that every time I clicked a random category image, the DOM will re-render and new random images will appear on the DOM. How do I prevent this re-render? Below is my codes.
const Categories = ({selectedCategory}) => {
const isLoading = useSelector(state => state.productsReducer.isLoading);
const productsByCategory = useSelector(state =>
state.productsReducer.productsByCategories);
const getRandomProductsByCategory = () => {
const randomProducts = []
for(let categories in productsByCategory) {
const randomCategory = productsByCategory[categories][getRandomIndex(productsByCategory[categories].length)];
productsByCategory[categories].map(category => {
if(category === randomCategory) {
randomProducts.push(category)
}
})
}
return randomProducts;
}
return (
<div class='categories-container'>
{getRandomProductsByCategory().map(randomProduct => (
<img onClick={selectedCategory} src={randomProduct.image} />}
</div>
)
}
function App() {
const dispatch = useDispatch();
const category = useSelector(state => state.productsReducer.category)
useEffect(() => {
dispatch(getProducts())
}, [dispatch])
const handleCategoryClick = ({target}) => {
return dispatch(getCategory(target.alt))
}
return (
<>
{/* <ProductsList /> */}
<Categories selectedCategory={handleCategoryClick} />
{category.map(product => <img src={product.image} />)}
</>
)
}
const populateProductsStarted = () => ({
type: 'POPULATE_PRODUCTS/fetchStarted'
})
const populateProductsSuccess = products => ({
type: 'POPULATE_PRODUCTS/fetchSuccess',
payload: products
})
const populateProductsFailed = error => ({
type: 'POPULATE_PRODUCTS/fetchFailed',
error
})
export const getCategory = (category) => ({
type: 'GET_CATEGORY',
category
})
const getProducts = () => async dispatch => {
dispatch(populateProductsStarted())
try {
const response = await fetch(url)
if(response.ok) {
let jsonResponse = await response.json();
return dispatch(populateProductsSuccess(jsonResponse))
}
} catch (err) {
dispatch(populateProductsFailed(err.toString()))
}
}
const initialState = {
isLoading: false,
isError: null,
allProducts: [],
productsByCategories: {},
category: []
}
const productsReducer = (state=initialState, action) => {
switch(action.type) {
case 'POPULATE_PRODUCTS/fetchStarted':
return {
...state,
isLoading: true
}
case 'POPULATE_PRODUCTS/fetchSuccess':
return {
...state,
isLoading: false,
allProducts: action.payload,
productsByCategories: action.payload.reduce((accumulatedProduct, currentProduct) => {
accumulatedProduct[currentProduct.category] = accumulatedProduct[currentProduct.category] || [];
accumulatedProduct[currentProduct.category].push(currentProduct);
return accumulatedProduct;
}, {})
}
case 'POPULATE_PRODUCTS/fetchFailed':
return {
...state,
isError: action.error
}
case 'GET_CATEGORY':
return {
...state,
category: state.allProducts.filter(product => product.category === action.category)
}
default:
return state
}
}
One way to achieve this is through memoization provided by React's useMemo.
const images = React.useMemo(getRandomProductsByCategory().map(randomProduct => (
<img onClick={selectedCategory} src={randomProduct.image} />, [productsByCategory])
return (
<div class='categories-container'>
{images}
</div>
)
This will keep the srcs consistent across re-renders.
I am trying to handle multiple inputs in a array of objects and each object I am mapping to the input field in a form .Now I am getting empty array I want to know where I am doing wrong .I am also sending post axios request and the value I am getting in input text fields through urlParmas using hooks.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { useLocation } from "react-router-dom";
import axios from "axios";
import { Button, TextField } from "#material-ui/core";
const MyComponent = () => {
const [sale, setSale] = useState("");
const [district, setDistrict] = useState("");
const obj = [
{
key: 1,
label: "Sales office",
handleChange: (e) => {
setSale(e.target.value);
},
val: sale,
},
{
key: 2,
label: "Sales district",
handleChange: (e) => {
setDistrict(e.target.value);
},
val: sale,
},
];
const [object, setObject] = useState(obj);
const handleSubmit = () => {
axios
.post("localhots:8000", {
sale,
district,
})
.then(() => {});
setSale("");
setDistrict("");
};
const handleComment = (e, item) => {
e.preventDefault();
let result = object;
result = result.map((el) => {
if (el.name === item.name) el.name = e.target.value;
return el;
});
console.log(result);
setObject(result);
};
const { search } = useLocation();
const urlParams = Object.fromEntries([...new URLSearchParams(search)]);
useEffect(() => {
const { salesoff } = urlParams;
setSale(salesoff);
}, []);
object.map((item, index) => {
return (
<div>
<form
onSubmit={(e) => {
e.target.reset();
}}
>
<TextField
name={item.name}
value={item.sale}
label={item.label}
onChange={handleChange}
/>
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
onClick={() => handleSubmit()}
>
Submit
</Button>
</form>
</div>
);
});
};
export default MyComponent;