I'm working with GoogleBooks API and I can get from the API the object that I need, and from there all the values I want. e.g.
console.log("item", item); // full object
console.log("title", item.volumeInfo.title); // title
but when I want to get nested objects I get an error:
console.log(item.volumeInfo.imageLinks.smallThumbnail);
TypeError: Cannot read property 'smallThumbnail' of undefined
So I created a this:
const BookItem = ({ item }) => {
const dispatch = useDispatch();
...
useEffect(() => {
const objSource = item.volumeInfo.imageLinks;
const getImages = Object.values(objSource);
console.log(getImages);
//eslint-disable-next-line
}, []);
const clickAddToStore = () => {
setAddToStore(true);
};
const clickRemoveFromStore = () => {
setAddToStore(false);
};
const addToDb = () => {
dispatch(
addToWooDb(
item.volumeInfo,
item.searchInfo,
stockQuantity,
bookStatus,
price
)
);
};
return (
<div
className="item3"
onClick={!addToStore ? clickAddToStore : clickRemoveFromStore}
>
{item.volumeInfo.title}
</div>
...
const mapStateToProps = (state) => ({
data: state.items,
loading: state.items.loading,
});
export default connect(mapStateToProps, { addToWooDb })(BookItem);
and works perfectly, hence I can see in the console:
["http://books.google.com/books/content?id=3nMEPQAAC…J&printsec=frontcover&img=1&zoom=5&source=gbs_api", "http://books.google.com/books/content?id=3nMEPQAAC…J&printsec=frontcover&img=1&zoom=1&source=gbs_api"]
0: "http://books.google.com/books/content?id=3nMEPQAACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api"
1: "http://books.google.com/books/content?id=3nMEPQAACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
But strangely I still get an error:
TypeError: Cannot convert undefined or null to object
I think that I can solve it with a promise, but I never understood how they work.
Any idea?
Thanks!
Related
I am trying to set up store in React using immer and borrowed the code from another project. However it doesn't work for me. Could be that immer version on borrowed project is lower than it is now.
Store looks like this:
const initialState: MyState = {
search: "No results yet"
}
const useValue = () => useState(initialState);
const { Provider, useTrackedState, useUpdate: useSetState } = createContainer(
useValue
);
const useSetDraft = () => {
const setState = useSetState();
return useCallback(
(draftUpdater: MyState) => {
setState(produce(draftUpdater));
},
[setState]
);
};
export { Provider, useTrackedState, useSetDraft, initialState }
and hook that i use to mutate state is:
const UseSetSearch = () => {
const setDraft = useSetDraft();
return useCallback((searchString: string) => {
setDraft((draft: MyState) => {
draft.search = searchString
return draft
})
}, [setDraft]);
}
With the store, I get error
Type 'MyState' provides no match for the signature '(state: WritableDraft): ValidRecipeReturnType'
and hook error is
Value of type '(draft: MyState) => MyState' has no properties in common with type 'MyState'. Did you mean to call it?
Store error is fixed when I do:
const useSetDraft = () => {
const setState = useSetState();
return useCallback(
(draftUpdater: MyState) => {
setState(produce(draftUpdater, draft => {
}));
},
[setState]
);
};
but hook error remains. What am I missing?
I'm trying to use React context to decide whether to have a button or not, and the problem is my context always runs twice and returns the first value as undefined -- the second time the output is correct.
Context:
const defaultMyContextValue = null;
export const myContext = createContext<GenericResponseFromEndpoint | null>(
defaultMyContextValue
);
export const fetcher = async (
endpoint: 'an_endpoint',
id: string
) => {
const client = new Client().ns('abcdef');
try {
return await client.rpc(
endpoint,
{
id
},
{}
);
} catch (err) {
// log error
return undefined;
}
};
export const MyContextProvider: React.FC = ({children}) => {
const context = useAnotherContext();
const {id} = useMatch();
const fetchMyContext = useCallback(
(endpoint: 'an_endppoint', id: string) =>
fetcher(endpoint, id),
[id]
);
const {data = null} = useSWR(
context?.name && ['an_endpoint', id],
fetchMyContext
);
return <myContext.Provider value={data}>{children}</myContext.Provider>;
};
export const useMyContext = () => {
const context = useContext(myContext);
if (context === undefined) {
throw new Error('------------------------');
}
return context;
};
Conditional render:
export const contextElement: React.FC = () => {
const info = useMYContext()?.info;
if (!info) {
return null;
// console.log('null');
}
console.log('not null');
// when i run it, it always returns two output scenarios:
// undefined - undefined
// undefine - expected value
// the front-end works as expected
return (
<div className="some-class-name">
<Button
variant="primary"
data-testid="button-123"
onClick={() => window.open('_blank')}
>'Do Something',
})}
</Button>
</div>
);
};
Test: my tests failed and the error is the system couldn't find the element. If I set the style={display:none} instead of returning null in the above conditional rendering -- it passes
describe('contextElement', () => {
const renderSomething = () => render(<contextElement />);
afterEach(() => {
myMock.mockClear();
});
it('renders button', () => {
myMock.mockResolvedValue({
info: 'some info'
});
const {getByTestId} = renderSomething();
expect(getByTestId('button-123')).toBeTruthy();
});
it('does not render button', () => {
myMock.mockResolvedValue(undefined);
const {getByTestId} = renderSomething();
expect(getByTestId('button-123')).not.toBeTruthy();
});
});
It's a really long post I know. Thanks for making it till the end. And yes, I check and there's no strictMode in my set-up. Thank you.
I have an array of country codes and I need to have the name.
I am trying to access the countries data from the state (axios call) and from there filter by country code, and from that new array, extract the common name of the country.
(I am using the restcountries.com api).
-If I create a new state to map from, I get the too many re-renders.
-Right now, Although the border countries info is there, I can't access it, I get the "Cannot read properties of undefined" error, that usually is tied to a lifecycle issue, therefore I am using a condition on when to access the information.
Still I am not able to get it stable and return the name that I need.
Can someone please take a look and tell me what am I doing wrong?
Thanks in advance
import axios from "axios";
const BorderCountries = (props) => {
const [countriesList, setCountriesList] = useState([]);
useEffect(() => {
axios
.get(`https://restcountries.com/v3.1/all`)
.then((countries) => setCountriesList(countries.data))
.catch((error) => console.log(`${error}`));
}, []);
const getCountryName = () => {
const codes = props.data;
const borderCountries = [];
codes.map((code) => {
const borderCountry = countriesList.filter((country) =>
country.cca3.includes(code)
);
borderCountries.push(borderCountry);
});
// console.log(borderCountries);
if (props.data.length === borderCountries.length) {
const borderName = borderCountries.map((border) =>
console.log(border[0].name.common)
);
return borderName
}
};
return (
<div>
<h3>Border Countries:</h3>
{getCountryName()}
</div>
);
};
export default BorderCountries;
const getCountryName = () => {
const codes = props.data;
if(countriesList.length === 0) return <></>;
const borderCountries = [];
codes.map((code) => {
const borderCountry = countriesList.filter((country) =>
country.cca3.includes(code)
);
borderCountries.push(borderCountry);
});
// console.log(borderCountries);
if (props.data.length === borderCountries.length) {
const borderName = borderCountries.map((border) =>
console.log(border[0].name.common)
);
return borderName
}
};
Try this, you forgot to wait for the call to finish.
I'm trying to understand how make a component that can remove itself from a array of components with functional components. Here is the sample code of what I'm trying to do:
const App = () => {
<ObjState>
<ObjectCreator />
<ObjectList />
</ObjState>
}
const ObjContext = createContext();
const ObjReducer = (state, { type, payload }) => {
switch(type) {
case Types.ADD_OBJ:
return {
...state,
objects: [...state.objects, payload]
};
case Types.REMOVE_OBJ:
return {
...state,
objects: state.objects.filter(obj => obj !== payload)
};
default:
return state;
}
}
const ObjState = ({ children }) => {
const initialState = {
objects: []
}
const [state, dispatch] = useReducer(ObjRecuder, initialState);
const addObj = (obj) => {
dispatch({
type: Types.ADD_OBJ,
payload: obj
});
}
const removeObj = (obj) => {
dispatch({
type: Types.REMOVE_OBJ,
payload: obj
});
}
return (
<ObjContext.Provider value={{
objects: state.objects,
addObj,
removeObj
}}>
{children}
</ObjContext.Provider>
);
}
const ObjCreator = () => {
const { addObject } = useContext(ObjContext);
const createObj =() => {
const obj = (<ObjectTypeA key={uuid()} />);
addObject(obj);
}
return (<button onClick={createObj}>create an object!</button>)
}
const ObjectList = () => {
const { objects } = useContext(ObjContext)
return (
<fragment>
{objects}
</fragment>
)
}
const ObjectTypeA = ({ key }) => {
const { removeObj } = useContext(ObjContext);
const removeSelf = () => {
removeObj(this);
}
return (
<button onClick={removeSelf}>remove me!</button>
)
}
The problem is you can't reference this in the final Object component.
I have the unique key but I'm not sure how to pass it through correctly. I attempted to build a reducer action that took the key from the Object and removed it that way but key came back as undefined even though it is deconstructed out of the props and I'm using an arrow function to preserve it.
I feel like I'm tackling this problem in the wrong way.
Issue
I think you veer off-course when trying to store what looks to be React components in your context state, you should be storing objects instead. The objects should have unique GUIDs. This allows the reducer to identify which object element to remove from state. The ObjectList should then render derived React components from the stored state.
I attempted to build a reducer action that took the key from the
Object and removed it that way but key came back as undefined even
though it is deconstructed out of the props and I'm using an arrow
function to preserve it.
This is because React keys (and refs) are not actually props. Keys can't be accessed in children components. You can can pass the same value via just about any other named prop though. Note below in solution I pass a React key and an id prop.
Solution
ObjectCreator: Creates objects, not React components
const ObjectCreator = () => {
const { addObj } = useContext(ObjContext);
const createObj = () => {
const obj = {
id: uuid()
};
addObj(obj);
};
return <button onClick={createObj}>create an object!</button>;
};
SpecificObject: passes its id to the removeObj callback.
const MyObject = ({ id }) => {
const { removeObj } = useContext(ObjContext);
const removeSelf = () => {
removeObj(id);
};
return (
<div>
<button onClick={removeSelf}>remove {id}</button>
</div>
);
};
ObjectList: renders the context objects mapped to JSX.
const ObjectList = () => {
const { objects } = useContext(ObjContext);
return (
<>
{objects.map((el) => (
<MyObject key={el.id} id={el.id} />
))}
</>
);
};
Check the passed id payload in the remove object reducer
const ObjReducer = (state, { type, payload }) => {
switch (type) {
case Types.ADD_OBJ:
return {
...state,
objects: [...state.objects, payload]
};
case Types.REMOVE_OBJ:
return {
...state,
objects: state.objects.filter((obj) => obj.id !== payload)
};
default:
return state;
}
};
Demo
Below is my code, which gets data from Redux:
function ProductsDetail(props){
const dispatch = useDispatch();
useEffect( () => {
const fetch = async () => {
const {data} = await axios.get("/api/products/" + props.match.params.id);
dispatch(
{
type: "product_detail_success",
payload: data
})
}
fetch();
return () => {};
}, [])
const {product} = useSelector((state) => {
return state.productDetails
});
console.log(product)
//console.log(product.image)
return (
<div>
</div>
)
}
The console.log(product) line prints out:
However, if I don't comment out the following console.log(product.image) line, an error of Uncaught TypeError: Cannot read property 'image' of undefined will occur.
image is just an attribute of product object , why printing it on the console makes the product object undefine?
Thanks in advence!
In the initial render product is undefined so:
console.log(product) // will show nothing
//console.log(product.image)
when it re-render:
`console.log(product)` // This show what you see in you log
So try the following code:
console.log(product) // will show nothing
console.log(product?.image) // Add ? to make sure only get image when product defined