How to pass data to react-mentions component using redux - javascript

I'm using https://github.com/signavio/react-mentions for React Mentions, a facebook #mentions-like frontend feature where a user can mention another user in the comment box.
Below is the code that gets data through fetch but couldn't get the data using redux and useSelector.
I used react-mentions and react-redux packages
Using fetchUsers query I can get the data from github and pass them to Mention component for '#' suggestions.
Now how to use redux instead of fetch to display suggestions for '#'? i.e, how to use callback passed to the getData function to get the hashtag suggestions?
import { useState } from 'react';
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
import { MentionsInput, Mention } from 'react-mentions';
import {
fetchHashTags,
} from './redux/tagsSlice';
function fetchUsers(query, callback) {
if (!query) return
fetch(`https://api.github.com/search/users?q=${query}`, { json: true })
.then(res => res.json())
// Transform the users to what react-mentions expects
.then(res =>
res.items.map(user => ({ display: user.login, id: user.login }))
)
.then(callback)
}
function AsyncGithubUserMentions({ value, data, onChange }) {
const dispatch = useDispatch();
const users = useSelector(
(state) => state?.tags?.users,
shallowEqual
);
const getData = (query, callback) => {
dispatch(fetchHashTags(query));
callback(users);
}
return (
<div className="async">
<h3>Async Github user mentions</h3>
<MentionsInput
value={value}
onChange={onChange}
placeholder="Mention any Github user by typing `#` followed by at least one char"
a11ySuggestionsListLabel={"Suggested Github users for mention"}
>
<Mention
displayTransform={login => `#${login}`}
trigger="#"
data={fetchUsers}
/>
<Mention
displayTransform={login => `#${login}`}
trigger="#"
data={getData}
/>
</MentionsInput>
</div>
)
}
export default AsyncGithubUserMentions
Redux code to fetch the hashtags from github:
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit';
import axios from 'axios';
export const fetchHashTags = createAsyncThunk(
'tags/hash',
async (value, { rejectWithValue, getState, dispatch }) => {
try {
const { data } = await axios.get(
`https://api.github.com/search/users?q=${query}`,
);
return data;
} catch (error) {
throw error;
}
}
);
const tagsSlice = createSlice({
name: 'tags',
initialState: {},
extraReducers: (builder) => {
builder.addCase(fetchHashTags.fulfilled, (state, action) => {
state.usertags = action?.payload;
});
},
});
export default tagsSlice.reducer;

Related

how to use redux thunks with redux toolkit in custom useFetch for fetching api data

im new to the redux toolkit here is my problem
im fetching data every time by creating own custom useFetch file for handling loading , success, error status
i used the same way with redux toolkit by creating createSlice method for accesing reducers and actions
this is working successfully and getting data from this way by dispaching actions and reducers
but i did't used the createAsyncThunk from redux toolkit
my confusion is is this currect way to fetch data from custom useFetch or should i use createAsyncthunk
Im not sure how to use createAsyncThunk in custom useFetch
if anyone knows the answer that is so appreciatable
posted my all files below
if i get answer with createAsyncThunk in custom useFetch that is soo appreciable
thanks advance
App.js
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import useFetchData from "./Components/useFetchData";
import { actions } from "./Slices/CounterSlice";
const App = () => {
useFetchData("https://jsonplaceholder.typicode.com/todos");
const apiData = useSelector((s) => s);
console.log(apiData.api);
return (
<>
{apiData.api.status !== "success" && <h1>hello</h1>}
{apiData.api.status === "success" &&
apiData.api.apiData.map((el) => {
return <h6 key={el.id}>{el.title}</h6>;
})}
</>
);
};
export default App;
custom useFetchData.js file
import React, { useCallback } from "react";
import { useDispatch } from "react-redux";
import { ApiActions } from "../Slices/ApiSlice";
const useFetchData = (link) => {
const dispatch = useDispatch();
const fetchData = useCallback(async () => {
try {
dispatch(ApiActions.loadingTime());
const getData = await fetch(link);
const toJson = await getData.json();
dispatch(ApiActions.successTime(toJson));
} catch {
dispatch(ApiActions.errorTime());
}
}, [link]);
React.useEffect(() => {
fetchData();
}, [fetchData]);
};
export default useFetchData;
this is createSlice file for creating actions and reducers
import { createSlice } from "#reduxjs/toolkit";
const apiSlice = createSlice({
name: "api",
initialState: {
status: "idle",
apiData: [],
error: false,
},
reducers: {
loadingTime: (state, action) => {
state.status = "Loading";
},
successTime: (state, action) => {
state.apiData = action.payload;
state.status = "success";
},
errorTime: (state, action) => {
state.apiData = [];
state.status = "error";
},
},
});
export const ApiActions = apiSlice.actions;
export const apiReducers = apiSlice.reducer;
You can use one way or the other. Example with createAsyncThunk:
export const checkIfAuthenticated = createAsyncThunk(
"auth/checkIsAuth",
async (_, thunkAPI) => {
const response = await axios.post(
"http://127.0.0.1:8080/dj-rest-auth/token/verify/",
{ token: localStorage.getItem("access") }
);
thunkAPI.dispatch(getUserInfo());
return response.data;
}
);
Then you handle loading, success, error status as before:
extraReducers: (builder) => {
builder
.addCase(checkIfAuthenticated.fulfilled, (state, action) => {
state.isAuthenticated = true;
})
.addCase(checkIfAuthenticated.rejected, (state, action) => {
state.isAuthenticated = false;
snackbar({ error: "Nie jesteÅ› zalogowany" });
})
},
However, if you have a lot of API queries, the optimal solution is to use redux toolkit query or react-query. These two libraries make queries a lot better.
If you care about code cleanliness and ease, check them out.

Adding dynamic data to Redux Toolkit's createApi from React Native Text Input?

Newer developer here. I've been stuck on this for far too long now and running out of places to turn!
Tech:
React Native
Redux Toolkit
I'm trying to take data from a Text Input on RN and add it to my RTK createApi call.
RTK website shows this:
export const pokemonApi = createApi({
reducerPath: 'pokemonApi',
baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
endpoints: (builder) => ({
getPokemonByName: builder.query({
query: (name) => `pokemon/${name}`,
}),
}),
})
I was hoping I could do something similar instead of ${name} being dynamic it would be my text input data? Has anyone seen examples of this? does the data have to hit a slice first then the API? Do I need createAsyncThunk?
trying not to be too verbose here is what I have so far. data is coming into the slice, but not sure where to go from there?
electedApi.js (API call)
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react';
import { useSelector, useDispatch } from 'react-redux';
import { getAddress } from '../../services/slices/addressSlice';
export const electedsApi = createApi({
reducerPath: 'newsApi',
baseQuery: fetchBaseQuery({
baseUrl: 'baseurl.help.com',
}),
endpoints: builder => ({
getElectedByAddress: builder.query({
const { address } = useSelector(state => state.address);
query: address => `${address}`,
}),
}),
});
addressSlice.js (state)
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
loading: false,
address: '',
};
export const addressSlice = createSlice({
name: 'address',
initialState,
reducers: {
getAddress: (state, action) => {
state.address = action.payload;
console.log(state.address + 'from slice');
},
},
});
export const { getAddress } = addressSlice.actions;
export default addressSlice.reducer;
Landing.js (form input)
import React, { useState } from 'react';
import { Text, Image, TouchableOpacity, TextInput, Alert } from 'react-native';
import { StyleSheet } from 'react-native';
import LinearGradient from 'react-native-linear-gradient';
import { useDispatch, useSelector } from 'react-redux';
import { getAddress } from '../../services/slices/addressSlice';
export default function Landing({ navigation }) {
const [text, setText] = useState();
const dispatch = useDispatch();
const handlePress = () => {
if (!text) {
Alert.alert('please enter address');
setText('');
} else {
Alert.alert('thanks');
dispatch(getAddress(text));
setText('');
navigation.navigate('Main');
}
};
return (
<TextInput
placeholderStyle={styles.input}
placeholder="your address"
onChangeText={text => setText(text)}
/>
<TouchableOpacity style={styles.button} onPress={handlePress}>
THANKS IN ADVANCE!!!
Your query endpoint cannot access state and should also not do so. RTK-Q determines that a new request should be made when the input to the hook changes - in your case there would be no input change and so a new request would never be made.
Just use it like this:
const address = useSelector(state => state.address.address)
const result = useGetElectedByAddressQuert(address)

Using redux with react-hooks without the connect HOC

Currently, I'm using functional components with hooks but still dispatching my actions with the connect HOC.
I read through the documentation with useDispatch but I'm unsure how to incorporate it in my code. From the examples, they are passing the the action types and payloads inside the component. Would I have to move myOfferActions functions back to the component in order to useDispatch?
MyOffers component
import React, { useEffect } from "react";
import { connect, useSelector } from "react-redux";
import "./MyOffers.scss";
import MyOfferCard from "../../components/MyOfferCard/MyOfferCard";
import { fetchMyOffers } from "../../store/actions/myOffersActions";
const MyOffers = (props) => {
const myOffers = useSelector((state) => state.myOffers.myOffers);
useEffect(() => {
props.fetchMyOffers();
}, []);
return (
<div className="my-offers-main">
<h1>My offers</h1>
{myOffers && (
<div className="my-offer-container">
{myOffers.map((offer) => (
<MyOfferCard key={offer.id} offer={offer} />
))}
</div>
)}
</div>
);
};
export default connect(null, { fetchMyOffers })(MyOffers);
offerActions
export const fetchMyOffers = () => async (dispatch) => {
const userId = localStorage.getItem("userId");
try {
const result = await axiosWithAuth().get(`/offers/${userId}`);
let updatedData = result.data.map((offer) => {
//doing some stuff
};
});
dispatch(updateAction(FETCH_MY_OFFERS, updatedData));
} catch (error) {
console.log(error);
}
};
offerReducer
import * as types from "../actions/myOffersActions";
const initialState = {
offerForm: {},
myOffers: [],
};
function myOffersReducer(state = initialState, action) {
switch (action.type) {
case types.FETCH_MY_OFFERS:
return {
...state,
myOffers: action.payload,
};
default:
return state;
}
}
export default myOffersReducer;
I don't think you need connect when using the redux hooks.
You just need to call useDispatch like:
const dispatch = useDispatch();
And use the function by providing the object identifying the action:
dispatch({ type: 'SOME_ACTION', payload: 'my payload'});
It should be working with redux-thunk too (I guess this is what you're using): dispatch(fetchMyOffers())

Redux - How to get data from store and post it

Newbie to Redux here, I have tried to follow a couple tutorials and I am not clear of how Redux actually works. It was mentioned that the store of Redux is to store the state of the whole tree. I have created and used actions, reducers, and store for my program and it works.
The question is, how do I retrieve what is in the store? Lets say after updating my component, how can I retrieve the value inside the component and to post it?
How can I know what changed in my dropdown list and to retrieve it?
Full code in Sandbox here https://codesandbox.io/s/elated-goldberg-1pogb
store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './RootReducer';
export default function configureStore() {
return createStore(
rootReducer,
applyMiddleware(thunk)
);
}
ProductsList.js
import React from "react";
import { connect } from "react-redux";
import { fetchProducts } from "./SimpleActions";
class ProductList extends React.Component {
constructor(props)
{
super(props);
this.state = {
selecteditems: '',
unitPrice: 0
}
}
componentDidMount() {
this.props.dispatch(fetchProducts());
}
componentDidUpdate(prevProps, prevState) {
if(prevState.selecteditems !== this.state.selecteditems)
{
this.setState((state, props) => ({
unitPrice: ((state.selecteditems * 1).toFixed(2))
}));
}
}
render() {
const { error, loading, products } = this.props;
if (error) {
return <div>Error! {error.message}</div>;
}
if (loading) {
return <div>Loading...</div>;
}
return (
<div>
<select
name="sel"
className="sel"
value={this.state.selecteditems}
onChange={(e) =>
this.setState({selecteditems: e.target.value})}
>
{products.map(item =>
<option key={item.productID} value={item.unitPrice}>
{item.itemName}
</option>
)}
</select>
<p>Unit Price: RM {this.state.unitPrice} </p>
</div>
);
}
}
const mapStateToProps = state => {
const products = state.productsReducer.items;
const loading = state.productsReducer.loading;
const error = state.productsReducer.error;
return {
products,
loading,
error,
}
};
export default connect(mapStateToProps)(ProductList);
SimpleAction.js
export function fetchProducts() {
return dispatch => {
dispatch(fetchProductsBegin());
return fetch('http://localhost:55959/api/products')
.then(handleErrors)
.then(res => res.json())
.then(results => {
dispatch(fetchProductsSuccess(results));
return results;
})
.catch(error => dispatch(fetchProductsFailure(error)));
};
}
function handleErrors(response) {
if(!response.ok) {
throw Error (response.statusText);
}
return response;
}
export const FETCHPRODUCTS_BEGIN = 'FETCHPRODUCTS_BEGIN';
export const FETCHPRODUCTS_SUCCESS = 'FETCHPRODUCTS_SUCCESS';
export const FETCHPRODUCTS_FAILURE = 'FETCHPRODCUTS_FAILURE';
export const fetchProductsBegin = () => ({
type: FETCHPRODUCTS_BEGIN
});
export const fetchProductsSuccess = products => ({
type: FETCHPRODUCTS_SUCCESS,
payload: {products}
});
export const fetchProductsFailure = error => ({
type: FETCHPRODUCTS_FAILURE,
payload: {error}
});
Thanks in advance!
You will need to pass your action handlers to connect function
connect(mapStateToProps,{actions})(ProductList).
how do I retrieve what is in the store? Lets say after updating my component, how can I retrieve the value inside the component and to post it?
if you want to see how is store change, you can add redux-logger to middleware to see that. when store change, it's likely a props change, you can handle this in function componentDidUpdate.
How can I know what changed in my dropdown list and to retrieve it?
values in dropdown is controlled by "const products = state.productsReducer.items;", productsReducer is controlled by actions you passed in dispatch like this: "this.props.dispatch(fetchProducts());".
I think you should add redux-logger to know more how to redux work, it show on console step by step. It will help you learn faster than you think :D
to retrieve it you forgot the selecteditems
const mapStateToProps = state => {
const products = state.productsReducer.items;
const loading = state.productsReducer.loading;
const error = state.productsReducer.error;
const selecteditems = state.prodcuts.selecteditems;
return {
products,
loading,
error,
selecteditems
};
};
To change it you should connect another function like
const mapDispatchToProps = dispatch => {
return {
onChangeDropdownSelection: (selected)=> dispatch(actions.setSelectedDropdown(selected))
}
}

Add value to React context from child components

I am trying to use React context as a state manager in my React Native app.
Here's the context:
import React, { createContext, useState, useEffect } from "react";
import axios from "axios";
export const GlobalContext = createContext();
export const Provider = ({ children }) => {
const [tracksList, setTracksList] = useState([
{
track_list: []
}
]);
useEffect(() => {
axios
.get(
`https://cors-anywhere.herokuapp.com/https://api.musixmatch.com/ws/1.1/chart.tracks.get?page=1&page_size=10&country=us&f_has_lyrics=1&apikey=${
process.env.REACT_APP_MM_KEY
}`
)
.then(res => {
setTracksList([
{
track_list: res.data.message.body.track_list
}
]);
})
.catch(err => console.log(err));
}, []);
return (
<GlobalContext.Provider value={[tracksList, setTracksList]}>
{children}
</GlobalContext.Provider>
);
};
export const Consumer = GlobalContext.Consumer;
Child component. Here I'd like to make an API call to get users and set this users field to global context. I can get context value from consumer, but how to set the new one?
import React, { useContext } from "react";
import { GlobalContext } from "../../context/context";
const Demo = () => {
const contextValue = useContext(GlobalContext);
console.log(contextValue, "Context outside from JSX");
return <div>Content</div>;
};
export default Demo;
So, is it possible to add new value to React context from every child component, like in Redux? Thanks in advance!
You could use the useReducer effect to achieve Redux reducers:
// Create context
export const ApiContext = React.createContext();
// Create some reducer function
const reducer = (state, action) => {
if (action.type === 'some action name') {
return {
...state,
report: action.payload,
};
}
return state;
};
// Overwrite a context provider
const Provider = ({ children }) => {
const [state, dispatch] = React.useReducer(reducer, {});
return (
<ApiContext.Provider
value={{
...state,
dispatch,
}}
>
{children}
</ApiContext.Provider>
);
};
Then you could use in components as follows:
const Component = () => {
const { dispatch, report } = React.useContext(ApiContext);
React.useEffect(() => {
const asyncPost = async () => {
const response = await fetch('some endpoint', {
method: 'POST',
});
const payload = await response.json();
// This will call a reducer action and update a state
dispatch({
type: 'some action name',
payload,
});
}
}, []);
...
};
So when Component is mounted, the state would be an empty object. Then when you update the state using the some action name action, the state becomes { report: some data from fetch }.

Categories

Resources