React Context for changing language - javascript

I want to change context on the language that client is choose, but the the value of the context didn't change. What am I doing wrong?
This is the Context:
import React from 'react'
const LanguageContext = React.createContext({
language: "EN",
setLanguage: () => { }
})
export default LanguageContext
and this where I change language:
import React, { useState, useContext } from 'react'
import styled from 'styled-components'
import LanguageContext from '../../Context'
const LanguageButton = () => {
const [isClicked, setIsClicked] = useState(false)
const {language, setLanguage} = useContext(LanguageContext)
const changeLanguage = () => {
setIsClicked(!isClicked)
}
return (
<span>
<Span>
<Button style={{ padding: 0 }} onClick={changeLanguage}>
Language - {language}
</Button>
</Span>
{isClicked ?
<Div style={{ position: 'absolute' }}>
<div><Button onClick={() => setLanguage('BG')} >BG - Bulgarian</Button></div>
<div><Button onClick={() => setLanguage('EN')}>EN - English</Button></div>
</Div> : ''
}
</span>
)
}

As there is no context provider in the component tree, the default value will be used, so setLanguage refers to setLanguage: () => { } which does nothing.
You'll need a component that actually stores the state, and then provides that to all subcomponents through the context:
function WithLanguage({ children }) {
const [language, setLanguage] = useState("");
return <LanguageContext.Provider value={{ language, setLanguage }}>{ children }</LanguageContext.Provider>;
}
/* in some top component*/
<WithLanguage>
<LanguageButton />
</WithLanguage>

Related

Getting toogleShow is not function when passing as props in react typescript functional component

I am getting error message like this toogleShow is not function whenever I am passing as props from child to parent component in react typescript functional component. In child, i am also declare an interface but still getting that error. I am not used to typescript with react but I am learning to use typescript with react.
App.tsx
import React, { useEffect, useState } from "react";
import logo from "./logo.svg";
import "./App.css";
import { useGetRecipesMutation } from "./services/recipeApi";
import Card from "./component/Card";
function App() {
const [query, setQuery] = useState("");
const [health, setHealth] = useState("vegan");
const [modal, setModal] = useState(false);
const [recipe, setRecipe] = useState({});
const [getRecipes, { data, isLoading }] = useGetRecipesMutation();
useEffect(() => {
getFoodRecipes();
}, [query, health]);
const getFoodRecipes = async () => {
await getRecipes({ query, health });
};
const toggleShow = (recipe: any) => {
setModal(!modal);
setRecipe(recipe);
};
const showModal = (recipe: any) => {
if (modal) {
return (
<>
<MDBModal show={modal} setShow={modal}>
<MDBModalDialog>
<MDBModalContent>
{recipe.label}
</MDBModalContent>
</MDBModalDialog>
</MDBModal>
</>
);
}
};
return (
<>
<div className="App">
<MDBRow className="row-cols-1 row-cols-md-3 g-4">
{data?.hits?.map((item: any) => (
<Card toggleShow={toggleShow} recipe={item.recipe} />
))}
</MDBRow>
{modal && showModal(recipe)}
</div>
</>
);
}
export default App;
Card.tsx
import React from "react";
import {
MDBCol,
MDBCardGroup,
MDBCard,
MDBCardImage,
MDBCardBody,
MDBCardTitle,
} from "mdb-react-ui-kit";
interface PropsFunction {
toggleShow: (item: any) => void;
}
const Card = (recipe: any, { toggleShow }: PropsFunction) => {
const { recipe: item } = recipe;
return (
<>
<MDBCol>
<MDBCardGroup>
<MDBCard className="h-100 mt-2 d-sm-flex">
<MDBCardImage
src={item.image}
alt={item.label}
position="top"
style={{ cursor: "pointer" }}
onClick={() => toggleShow(item)}
/>
<MDBCardBody>
<MDBCardTitle>{item.label}</MDBCardTitle>
</MDBCardBody>
</MDBCard>
</MDBCardGroup>
</MDBCol>
</>
);
};
export default Card;
Try declaring your Card component like this:
const Card: React.FC<PropsFunction> = ({recipe, toggleShow}) => {
And change your interface to:
interface PropsFunction {
toggleShow: (item: any) => void;
recipe: any;
}

How to get value of a component to another component

I am creating a Project where I need to get value of a component state value to another component.
How can I get the value?
Information
Both are functional component.
I need to send data from A (state) to B(state).
A state data are set from react dropdown menu.Which is a button.
I don't use Link or Route to A dropdown that's why I can't get it with useParams()
I need to set value in B component which will fetch data with passing language.
I have import all needed things & don't have any warnings & error.
Code of component A
Send value from this language state to B
const A = () => {
const [language, setLanguage] = useState('en');
return (
<Navbar expand="xl" bg='light' expand={false}>
<Container>
<DropdownButton id="dropdown-basic-button" title={<MdOutlineLanguage />}>
<Dropdown.Item as="button" onClick={() => setLanguage('en')}>English</Dropdown.Item>
<Dropdown.Item as="button" onClick={() => setLanguage('ar')}>العربية</Dropdown.Item>
<Dropdown.Item as="button" onClick={() => setLanguage('bn')}>বাংলা</Dropdown.Item>
</DropdownButton>
</Container>
</Navbar>
)
};
export default A
Code of Component B
I need to get here A state value & set it to B state. Then pass to useGetDataQuery & fetch data.
const B = () => {
let [language, setLanguage] = useState('en')
const { data } = useGetDataQuery({language })
return (
<>
</>
)
}
export default B
Redux Section
I'm using readux & #reduxjs/toolkit to store fetch data. Can I store my language data to here. Than how can get to from anywhere of my component.
react-rotuer-dom v6
export default configureStore({
reducer: {
[dataOne.reducerPath]: dataOne.reducer,
[data2.reducerPath]: dataTwo.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: false
}).concat([dataOne.middleware, dataTwo.middleware]),
})
Maybe instead of using useState, you can use a global state by using useContext because I think your language will be use on several places as request body and of course to edit the state value, you can combine it with useReducer.
// UPDATE
working code: https://codesandbox.io/s/sleepy-monad-21wnc
MyContext
import { useReducer, useContext, createContext } from "react";
const initialValue = {
language: "id"
// other: "value",
};
const AppContext = createContext(initialValue);
const AppReducer = (state, action) => {
switch (action.type) {
case "CHANGE_LANGUAGE":
return {
...state,
...action.payload
};
default:
return state;
}
};
const MyContext = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialValue);
return (
<AppContext.Provider value={[state, dispatch]}>
{children}
</AppContext.Provider>
);
};
export const useAppContext = () => useContext(AppContext);
export default MyContext;
this is the context and reducer component later on use in App.js
App.js
import A from "./A";
import B from "./B";
import MyContext from "./MyContext";
import "./styles.css";
export default function App() {
return (
<MyContext>
<div className="App">
<A />
<B />
</div>
</MyContext>
);
}
A.js
import { useAppContext } from "./MyContext";
const A = () => {
const [globalState, dispatch] = useAppContext();
const onChange = (e) => {
dispatch({
type: "CHANGE_LANGUAGE",
payload: {
[e.target.name]: e.target.value
}
});
};
return (
<>
<p>Start Of Component A </p>
<input value={globalState.language} name="language" onChange={onChange} />
<p>End Of Component A </p>
</>
);
};
export default A;
B.js
import { useAppContext } from "./MyContext";
const B = () => {
const [globalState, dispatch] = useAppContext();
return (
<>
<p>Start Of Component B </p>
<h2>Language Val : {globalState.language}</h2>
<p>End Of Component B </p>
</>
);
};
export default B;

How to test for props initial values?

I have a header component which listens for loggedInUser data from Redux store. I want to unit test for redux prop values. Like i have mocked a redux store for initial values and want to test for those values in props of the connected component.
import React, { useState } from 'react';
import { connect } from 'react-redux';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faUser, faShoppingCart } from '#fortawesome/free-solid-svg-icons';
import { NavLink, useHistory } from 'react-router-dom';
import Cart from './../Cart/Cart.component';
import { signOutStart } from './../../redux/user/user.actions';
import './Header.styles.scss';
export const Header = ({ noOfItemsInCart, loggedInUser, signOut }) => {
const [isUserDropDownVisible, setUserDropDownVisibility] = useState(false);
const [isCartDropDownVisible, setCartDropDownVisibility] = useState(false);
const history = useHistory();
console.log(loggedInUser);
return (
<header className = 'header' id = 'header'>
<NavLink to = '/'><p className = 'title'>Kart</p></NavLink>
{loggedInUser ? (
<div className = 'header__options' id = 'header__options'>
<div className = 'cart__options'>
<FontAwesomeIcon
icon={faShoppingCart}
onClick = {() => {
setUserDropDownVisibility(false);
setCartDropDownVisibility(prevState => {return !prevState})}
} />
<span><sup>{noOfItemsInCart}</sup></span>
{isCartDropDownVisible ? (
<div className="dropdown">
<Cart />
</div>
) : null}
</div>
<div className = 'user__options'>
<FontAwesomeIcon
icon={faUser}
onClick = {() => {
setCartDropDownVisibility(false);
setUserDropDownVisibility(prevState => {return !prevState})}
} />
{isUserDropDownVisible ? (
<div className="dropdown" onClick = {() => setUserDropDownVisibility(false)}>
<NavLink to = '/orders'>My Orders</NavLink>
<span onClick = { async () => {
await signOut();
history.push('/auth');
} }>Logout</span>
</div>
) : null}
</div>
</div>
) : null}
</header>
)
}
const mapStateToProps = state => {
return {
loggedInUser: state.user.loggedInUser,
noOfItemsInCart: state.cart.noOfItemsInCart
}
}
const mapDispatchToProps = dispatch => {
return {
signOut: () => dispatch(signOutStart())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header);
I had implemented an unit test as follows, by using a shallow render of component and tried accessing the props using .props()
import React from 'react';
import { shallow } from 'enzyme';
import configureMockStore from 'redux-mock-store';
import Header from './Header.component';
const mockStore = configureMockStore();
describe('<Header />', () => {
let wrapper, store;
beforeEach(() => {
const initialState = {
user: {
loggedInUser: 'user1',
error: null
},
cart: {
noOfCartItemsInCart: 0
}
}
store = mockStore(initialState);
wrapper = shallow(
<Header store = {store} />
)
});
it('should have valid props', () => {
expect(wrapper.props().loggedInUser).toBe('user1');
})
})
I am getting prop values a undefined or null values. How to test for prop values to an redux connected component?
Have you tried this from the docs?
wrapper.instance().props

Can I consolidate multiple functions that set state based on callbacks from React child components?

I'm using React hooks to set state. There is a parent component that has multiple child components. The parent component has the state, and passes functions to the children components to update its state as callbacks.
The child components are the same, they just receive different function callbacks to update the related state in the parent.
My question is, can I write one handleChange function in the parent that will allow me to use this function callback structure to set multiple state values in the parent?
Parent component:
import React, { useState } from 'react'
import Control from './Control'
const Sort = () => {
const [controlUpValues, setControlUpValues] = useState([])
const [controlDownValues, setControlDownValues] = useState([])
const handleControlUpChange = values => {
setControlUpValues(values)
}
const handleControlDownChange = values => {
setControlDownValues(values)
}
return
<>
<Control
setControlItems={handleControlUpChange}
/>
<Control
setControlItems={handleControlDownChange}
/>
</>
)
}
export default Sort
Child component:
import React, { useState } from 'react'
import { Button, TextField } from '#material-ui/core'
function Control({ setControlItems }) {
const [controlInputValues, setControlInputValues] = useState([])
const [inputRef, setInputRef] = useState([])
const [inputValues, setInputValues] = useState([])
const handleValueChange = () => setInputValues(inputRef.value)
const addValuesToItems = () => {
setControlItems(inputValues)
}
return (
<div>
<TextField
inputRef={ref => setInputRef(ref)}
value={controlInputValues ? controlInputValues : ''}
onChange={handleValueChange}
/>
<Button
onClick={addValuesToItems}
>
Add
</Button>
</div>
)
}
export default Control
You can have an object containing the functions to update the state :
Parent
import React, { useState } from 'react'
import Control from './Control'
const Sort = ({ classes }) => {
const [controlBoostValues, setControlBoostValues] = useState([])
const [controlBuryValues, setControlBuryValues] = useState([])
const functions = {
boost: setControlBoostValues,
bury: setControlBuryValues
}
const handleChange = (key, values) => functions[key](values);
return
<>
<Control
setControlItems={handleChange}
/>
<Control
setControlItems={handleChange}
/>
</>
)
}
Child :
import React, { useState } from 'react'
import { Button, TextField } from '#material-ui/core'
function Control({ setControlItems }) {
const [controlInputValues, setControlInputValues] = useState([])
const [inputRef, setInputRef] = useState([])
const [inputValues, setInputValues] = useState([])
const handleValueChange = () => setInputValues(inputRef.value)
const addValuesToItems = () => {
setControlItems("boost" , inputValues)
}
return (
<div>
<TextField
inputRef={ref => setInputRef(ref)}
value={controlInputValues ? controlInputValues : ''}
onChange={handleValueChange}
/>
<Button
onClick={addValuesToItems}
>
Add
</Button>
</div>
)
}
export default Control

reactjs continuous re rendering

I was watching Wes Bos' speech recognition application, and thought of developing that in React.
So I started and everything was going great, I used hooks and context for state management, but then I got stuck so here what is really happening:
So as the user says any color name that is mentioned, the app strike-through the name of the color (add a css class to that color).
But as soon as another name is called the already striked through color becomes inactive (removes the css class from the previous color name and add the css class to the new one), because that component (ColorList) is getting re-rendered again and again.
I want to persist that strike-through on that individual color name.
Colors component (Which renders all the list of colors)
import React, { useState, useEffect, useContext } from "react";
import { GlobalContext } from "../context/GlobalState";
import { colors } from "../utils/colors";
import { ColorList } from "./ColorList";
const Colors = () => {
const [colors1, setColors] = useState([]);
const colorArray = Object.keys(colors).sort((a, b) => a.length - b.length);
useEffect(() => {
setColors(colorArray);
}, []);
const { color, already } = useContext(GlobalContext);
// console.log("context", context.color);
return (
<div className="colors" style={{ backgroundColor: "green" }}>
{colors1.map((colors, index) => (
<ColorList key={index} colors={colors} />
))}
</div>
);
};
export default Colors;
Color List Component
import React, { useContext } from "react";
import { isDark } from "../utils/colors";
import { GlobalContext } from "../context/GlobalState";
export const ColorList = props => {
const { color} = useContext(GlobalContext);
return (
<span
className={`color ${isDark(props.colors) && "dark"} ${props.colors} ${
props.colors === color ? "got" : ""
}`}
style={{ backgroundColor: `${props.colors}` }}
>
{props.colors}
</span>
);
};
GlobalState Context
import React, { createContext, useReducer } from "react";
import AppReducer from "./AppReducer";
const initialState = {
colorName: "",
alreadyDone: []
};
export const GlobalContext = createContext(initialState);
export const GlobalState = ({ children }) => {
const [state, dispatch] = useReducer(AppReducer, initialState);
function changeColorName(color) {
dispatch({
type: "CHANGE",
payload: color
});
}
function addToAlready(color) {
dispatch({
type: "ADDTO",
payload: color
});
}
console.log("from Context", state);
return (
<GlobalContext.Provider
value={{
color: state.colorName,
changeColor: changeColorName,
alreadyDone: state.alreadyDone,
already: addToAlready
}}
>
{children}
</GlobalContext.Provider>
);
};

Categories

Resources