How to integrate React MD autocomplete with redux? - reactjs

I want to integrate react-md with redux, but I don't understand how to trigger the onAutocomplete function. For now I only want to get some hard coded data from the Action, later on I'll add an api call and the search text as parameter.
Here is my action with the hard coded data that I want to dispatch:
export const searchCityAutoComplete = () => {
// no need for text parameter to search at this point
const users = [{
id: '1',
name: 'Robin',
}, {
id: '2',
name: 'Yan',
}]
return {
type: "AUTOCOMPLETE_SEARCH",
payload: users
};
}
Here is the reducer:
const initState = {
searchResults: [],
}
const sitesReducer = (state = initState, action) => {
switch (action.type) {
case "AUTOCOMPLETE_SEARCH":
state = {
...state,
searchResults: action.payload
}
break;
default:
return state;
}
return state;
}
export default sitesReducer;
And here is the component
import React from 'react';
import { connect } from 'react-redux';
import { searchCityAutoComplete } from '../actions/sitesActions';
import Autocomplete from 'react-md/lib/Autocompletes';
const SearchAutocomplete = ({ searchResults, onAutocomplete }) => (
<div >
<div className="md-text-container" style={{ marginTop: "10em" }}>
<Autocomplete
id="test-autocomplete"
label="Autocomplete"
dataLabel="name"
autocompleteWithLabel
placeholder="Search Users"
data={searchResults}
onAutocomplete={(...args) => {
searchCityAutoComplete(args)
console.log(args);
}}
deleteKeys={[
"id",
]}
simplifiedMenu={false}
anchor={{
x: Autocomplete.HorizontalAnchors.CENTER,
y: Autocomplete.VerticalAnchors.TOP
}}
position={Autocomplete.Positions.BOTTOM}
/>
</div>
</div>
);
const mapStateToProps = state => {
console.log(state)
return {
searchResults: state.sitesReducer.searchResults,
}
}
const mapDispatchToProps = dispatch => ({
onAutocomplete: () => { dispatch(searchCityAutoComplete()) }
})
export default connect(mapStateToProps, mapDispatchToProps)(SearchAutocomplete);
As you probably notice, the onAutocomplete function isn't in the same scope as the component... so I guess that's why it's not triggered. For a starting point I just need to get the data from the action - once I type in the autocomplete text box...thanks.

From react-md docs :
onAutocomplete : An optional function to call when an autocomplete suggestion is clicked either by using the mouse, the enter/space key,
or touch.
And so onAutocomplete is only called when you select a suggestion. And it's not what you're looking for. What you're looking for is the onChange prop :
onChange : An optional function to call when the Autocomplete's text field value changes.
Here you can find a simple example code : https://codesandbox.io/s/muddy-cdn-l85sp

You can just pass your onAutocomplete action straight into Autocomplete component:
const SearchAutocomplete = ({ searchResults, onAutocomplete }) => (
<div>
<div className="md-text-container" style={{ marginTop: "10em" }}>
<Autocomplete
id="test-autocomplete"
label="Autocomplete"
dataLabel="name"
autocompleteWithLabel
placeholder="Search Users"
data={searchResults}
onAutocomplete={onAutocomplete} // Pass the action from props here
deleteKeys={[
"id",
]}
simplifiedMenu={false}
anchor={{
x: Autocomplete.HorizontalAnchors.CENTER,
y: Autocomplete.VerticalAnchors.TOP
}}
position={Autocomplete.Positions.BOTTOM}
/>
</div>
</div>
);
Then in mapDispatchToProps you'll need to accept autocomplete value and do a search on it or set it to reducer:
const mapDispatchToProps = dispatch => ({
onAutocomplete: (value) => dispatch(searchCityAutoComplete(value))
})
export const searchCityAutoComplete = (value) => {
// do smth with the value
const users = [{
id: '1',
name: 'Robin',
}, {
id: '2',
name: 'Yan',
}]
return {
type: "AUTOCOMPLETE_SEARCH",
payload: users
};
}

Related

How do I create a delete/clear button in REACT js?

Hi I'm new to REACT and I have a HW where I need to create a grocery shopping list and I need to create a clear button. The isPurchased key value pair is a boolean though. I need to create a button that when I click Purchased it clears that grocery item off my list. Any help would be appreciated.
class App extends Component {
state = {
grocery: grocery,
item: '',
brand: '',
units: Number,
quantity: Number,
isPurchased: Boolean
}
handleChange = (e) => {
this.setState({ [e.target.id]: e.target.value })
}
handleSubmit = (e) => {
e.preventDefault()
const addGrocery = {
item: this.state.item,
brand: this.state.brand,
units: this.state.units,
quantity: this.state.quantity,
}
this.setState({
grocery: [addGrocery, ...this.state.grocery],
item: '',
brand: '',
units: Number,
quantity: Number,
})
const removeGrocery = {
item: this.state.item
}
}
hey here is a full code for creating a to do list in react (it will be very similar to your problem):
**
Summary
** of the idea of creating a to-do list or shopping list is that each to-do will be an object, when we create a new object we will insert it into an array. once it is in the array by using the array.map() function we will convert each object to an HTML element to make the UI.
if something is unclear I am here to answer
file - App.js:
import React, { useState, useReducer } from "react";
import Todo from "./Todo";
export const ACTIONS = {
ADD_TODO: "add-todo",
TOGGLE_TODO: "toggle-todo",
DELETE_TODO: "delete-todo",
};
function reducer(todos, action) {
switch (action.type) {
case ACTIONS.ADD_TODO:
return [...todos, newTodo(action.payload.name)];
case ACTIONS.TOGGLE_TODO:
return todos.map((todo) => {
if (todo.id === action.payload.id) {
return { ...todo, complete: !todo.complete }; //change to complete if we found to id that toggled
}
return todo;
});
case ACTIONS.DELETE_TODO:
return todos.filter((todo) => todo.id !== action.payload.id);
default:
return todos;
}
}
function newTodo(name) {
return { id: Date.now(), name: name, complete: false };
}
const App = () => {
const [todos, dispatch] = useReducer(reducer, []); //useReducer return the state and the reducer function
const [name, setName] = useState("");
function handleSubmit(e) {
e.preventDefault();
dispatch({ type: ACTIONS.ADD_TODO, payload: { name: name } });
setName("");
}
return (
<>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</form>
{todos.map((todo) => {
return <Todo key={todo.id} todo={todo} dispatch={dispatch} />;
})}
</>
);
};
export default App;
Another file (component) - Todo.js:
import React from "react";
import { ACTIONS } from "./App";
const Todo = ({ todo, dispatch }) => {
return (
<div>
<span style={{ color: todo.complete ? "#AAA" : "#000" }}>
{todo.name}
</span>
<button
onClick={() =>
dispatch({ type: ACTIONS.TOGGLE_TODO, payload: { id: todo.id } })
}
>
Toggle
</button>
<button
onClick={() =>
dispatch({ type: ACTIONS.DELETE_TODO, payload: { id: todo.id } })
}
>
Delete
</button>
</div>
);
};
export default Todo;

Why is the component fully re-rendering when updating a single state through context?

I have created a page which has two columns:
In one column the idea is to display a list of items
On the other column, I should show some info related to the selected item
The code I have so far is:
import { INavLink, INavLinkGroup, INavStyles, Nav } from "#fluentui/react";
import React, { createContext, useContext, useState } from "react";
interface HistoryTtem {
id: string;
}
interface AppState {
selectedItem: string | undefined;
updateSelectedItem: (value: string | undefined) => void;
items: Array<HistoryTtem>;
}
const AppContext = createContext<AppState>({
selectedItem: undefined,
updateSelectedItem: (value: string | undefined) => {},
items: []
});
const App = () => {
const Column1 = () => {
const rootState: AppState = useContext(AppContext);
const getNavLinks: Array<INavLink> = rootState.items.map((item) => ({
name: item.id,
key: item.id,
url: ""
}));
const groups: Array<INavLinkGroup> = [
{
links: getNavLinks
}
];
const navStyles: Partial<INavStyles> = {
root: {
boxSizing: "border-box",
border: `1px solid #eee`,
overflowY: "auto"
}
};
const onItemClick = (
e?: React.MouseEvent<HTMLElement>,
item?: INavLink
) => {
if (item && item.key) {
rootState.updateSelectedItem(item.key);
}
};
return (
<Nav
onLinkClick={onItemClick}
selectedKey={rootState.selectedItem}
ariaLabel="List of previously searched transactions"
styles={navStyles}
groups={groups}
/>
);
};
const Column2 = () => {
return <div>aaa</div>;
};
const [historyItems, setHistoryItems] = useState<Array<HistoryTtem>>([
{
id: "349458457"
},
{
id: "438487484"
},
{
id: "348348845"
},
{
id: "093834845"
}
]);
const [selectedItem, setSelectedItem] = useState<string>();
const updateSelectedItem = (value: string | undefined) => {
setSelectedItem(value);
};
const state: AppState = {
selectedItem: selectedItem,
updateSelectedItem: updateSelectedItem,
items: historyItems
};
return (
<AppContext.Provider value={state}>
<div>
<Column1 />
<Column2 />
</div>
</AppContext.Provider>
);
};
export default App;
As you can see, I have a root state which will serve to drive the update of the second column triggered from inside the first one. But it is not working. When I click on an item, the whole component in the first column is re-rendering, while it should only change the selected item.
Please find here the CodeSandbox.
You shouldn't nest component functions.
The identity of Column1 changes for every render of App since it's an inner function, and that makes React think it needs to reconcile everything.
Move Column1 and Column2 up to the module level.
What makes react rerender is two things:
Change in State
Change in Props
You have an App Component which is the root of your components and it has a selectedItem state which is changing when an item is clicked so you have a new state and the new state will cause rerender

How to make dynamic tab view screen in react native

I am trying to add tabs in my react native app. Here on tab i want to show all the data coming from an api. This gives a array of string. And when user click on any tab it should show respective data. Here is an example image.
Here below header I want to display the array of string coming from the ap.
Below the search field I want to display the data which is coming from different api.
I am using a package https://www.npmjs.com/package/react-native-tab-view . I am not sure how to achieve this with this.
Here is the code I have
import { TabView, SceneMap } from "react-native-tab-view";
import { connect } from "react-redux";
import { getAllState } from "../../actions/hubActions";
interface CommunityMemberProps {
getStates: () => void;
allStates: [];
}
const styles = StyleSheet.create({
scene: {
flex: 1,
},
});
const FirstRoute = () => (
<View style={[styles.scene, { backgroundColor: "#ff4081" }]} />
);
const SecondRoute = () => (
<View style={[styles.scene, { backgroundColor: "#673ab7" }]} />
);
const initialLayout = { width: Dimensions.get("window").width };
const CommunityMember = ({ getStates, allStates }: CommunityMemberProps) => {
useEffect(() => {
getStates();
}, []);
const [searchText, setSearchText] = useState<string>("");
const handleChangeText = (text: string) => {
setSearchText(text);
};
console.log("allStates", allStates); <-- this gives data ["India", "newDelhi"]
const [index, setIndex] = React.useState(0);
const [routes] = React.useState([
{ key: "First", title: "First" },
{ key: "Second", title: "Second" },
]);
const renderScene = SceneMap({
first: FirstRoute,
second: SecondRoute,
});
return (
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
/>
);
};
function mapStateToProps(state: any) {
return {
allStates: state.hub.allStates,
};
}
const mapDispatchToProps = (dispatch: any) => ({
getStates: () => dispatch(getAllState()),
});
export default connect(mapStateToProps, mapDispatchToProps)(CommunityMember);
Since you are using redux this can be easily done. Whenever you select a tab update the redux state with the tab selected. Keep the component same for all tabs and retrieve the tab from redux state using mapStateToProps and fetch data dynamically and useEffect or componentDidMount() hook.

How can I access state inside of my Redux cartReucer?

I need to access my current cart state which is just a list of products that have
been added to the cart, so that I check ids in order to calculate quantity for duplicate products. I realize that one of my issues here is that i've initialized itemsInCart with an empty array but i'm not sure what else to do here since, state can't be destructured without it.
cartReducer.js
const itemsInCart = []
export const cartReducer = (state = itemsInCart, action) => {
const { type, payload } = action;
switch (type) {
case "ADD_TO_CART":
return [
...state,
{
imgUrl: payload.imgUrl,
name: payload.name,
price: payload.price,
quantity: payload.quantity
},
];
default:
}
return state;
};
Product.js
Clicking the button dispatches the 'ADD_TO_CART' action, adds new products to our cart in state.
import React from 'react';
import {useDispatch} from 'react-redux';
const Product = ({imgUrl, name, price, id }) => {
const dispatch = useDispatch()
const addToCart = () => {
dispatch({
type: "ADD_TO_CART",
payload: {
imgUrl: imgUrl,
name: name,
price: price,
id: id
}
})
}
return (
<div
key={id}
style={{
textAlign: "center",
display: "flex",
border: "1px solid",
marginBottom: "2rem",
flexDirection: 'column'
}}
>
<img
src={imgUrl}
style={{ height: "5rem", width: "5rem" }}
alt="The product"
/>
<h3>{name}</h3>
<p>${price}.00</p>
{id}
<button onClick={()=>addToCart()}>Add To Cart</button>
</div>
);
}
export default Product;
InCartList.js
Renders list of items in my cart inside CartContainer
import React from "react";
import { useSelector } from "react-redux";
import CartItem from "./CartItem";
const ProductList = () => {
const allState = useSelector((state) => state.cart);
const renderProducts = () => {
return allState.map((product) => {
return (
<CartItem id={product.id} quantity={product.quantity}key={product.id} name={product.name} price={product.price}/>
);
});
};
return <>{renderProducts()}</>;
};
export default ProductList;
You shouldn't place any logic inside reducer (reducer should only pure function)
You can try to get state you want before dispatch action ADD_TO_CART
use getStateToProps function
use store which should be exported when initialized inside App component (I guess)
export const store = configureAppStore(history);

passing parameter onClick, React

i have this slice of code:
const ChangeRequest = ({
inRelease,
title,
requestId,
cost,
supervisor,
chipText,
description,
showModal,
}) => (
<div
onClick={() => showModal({
modal: 'changeRequestDetail',
state: { title },
})}
className={styles.crContainer}
>
Main problem
in the const ChangeRequest i've the value title, i need to pass this value to the onClick property of the modal.
modal
<div
onClick={() => showModal({
modal: 'changeRequestDetail',
state: { title },
})}
className={styles.crContainer}
>
In this way if i try to console.log(title) i get undefined as value.
Question
how can i pass the value of title to the value state of show modal in the onClick function ?
Added the showModal
so i called it with mapDispatchToProps
const mapDispatchToProps = {
showModal: showModalAction,
};
this is the relative action
export const SHOW_MODAL = 'SHOW_MODAL';
export const HIDE_MODAL = 'HIDE_MODAL';
export function showModalAction(params) {
return dispatch => dispatch({
type: SHOW_MODAL,
payload: params,
});
}
export function hideModalAction(params) {
return dispatch => dispatch({ type: HIDE_MODAL });
}
this is his reducer:
import { Map } from 'immutable';
import { SHOW_MODAL, HIDE_MODAL } from '../actions/modalActions';
const initialState = Map({
type: null,
state: null,
});
const modalReducer = (state = initialState, action) => {
switch (action.type) {
case SHOW_MODAL:
return state
.set('type', action.payload.modal)
.set('state', action.payload.state);
case HIDE_MODAL:
return state
.set('type', null)
.set('state', null);
default:
return state;
}
};
export default modalReducer;
Yesterday, after several tries i succeed to got the Data.
What was the problem ?
we need to mapStateToProps the data from the state of the modal. Why ? because the modal was a reducer.
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import styles from './styles.module.css';
import BlueLightRow from './BlueLightRow';
import DetailSection from './DetailSection';
import Modal from '../../../components/Modal';
import Header from './Header';
function ChangeDetail({
title, supervisor, architect, initiator, id, cost, description, chipText,
}) {
return (
<Modal noPadding bigDialog>
{console.log(title)}
<div className={styles.blueBackground}>
<div className={styles.container}>
<Header title={title} chipText={chipText} />
<BlueLightRow cost={cost} id={id} />
<DetailSection supervisor={supervisor} architect={architect} claimant={initiator} description={description} />
</div>
</div>
</Modal>
);
}
ChangeDetail.propTypes = {
title: PropTypes.string.isRequired,
architect: PropTypes.string.isRequired,
initiator: PropTypes.string.isRequired,
supervisor: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
cost: PropTypes.number.isRequired,
description: PropTypes.string.isRequired,
chipText: PropTypes.string.isRequired,
};
const mapStateToProps = state => ({
title: state.getIn(['modalReducer', 'state', 'title']),
architect: state.getIn(['modalReducer', 'state', 'architect']),
initiator: state.getIn(['modalReducer', 'state', 'initiator']),
supervisor: state.getIn(['modalReducer', 'state', 'supervisor']),
description: state.getIn(['modalReducer', 'state', 'description']),
cost: state.getIn(['modalReducer', 'state', 'cost']),
id: state.getIn(['modalReducer', 'state', 'requestId']),
chipText: state.getIn(['modalReducer', 'state', 'chipText']),
});
export default connect(mapStateToProps, null)(ChangeDetail);
If someone will need more explanation about the code i will be happy to go deep if i can :)

Resources