Я некоторое время борюсь с этой проблемой, и я не нашел и не понял решения в Интернете, потому что я новичок в ReactJS. И вот я здесь.
Контекст
Я пытаюсь разработать цоколь для приложения с Symfony 3.4 и React 16.8, с рендерингом как сервера, так и клиента.
Для этого я использовал limenius/react-bundle
, чтобы сделать ссылку.
Я использовал тонны кода из этого примера: https://github.com/Limenius/symfony-react-sandbox.
Я покажу вам, какую версию я использую соответствующего пакета:
composer.json
"php": ">=7.2.2",
// Webpack
"symfony/webpack-encore-bundle": "^1.6",
"symfony/webpack-encore-pack": "^1.0",
// Limenius
"limenius/liform-bundle": "^0.13.0",
"limenius/react-bundle": "^3.0",
package.json
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/preset-react": "^7.0.0",
"babel-polyfill": "^6.26.0",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"liform-react": "^0.9.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-on-rails": "^11.3.0",
"react-redux": "^7.1.0",
"react-router-dom": "^5.0.1",
"redux": "^4.0.1",
"redux-form": "^8.2.3",
"redux-persist": "^5.10.0",
"redux-thunk": "^2.3.0",
Задача
Когда я пытаюсь использовать компонент <Liform/>
из пакета liform-react
, внутри моего приложения React я получаю следующую ошибку:
Uncaught Invariant Violation: Could not find "store" in either the context or props of "Connect(Form(BaseForm))". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Form(BaseForm))".
at invariant (http://dev.dummyreact/build/js/app.js:3662:15)
at new Connect (http://dev.dummyreact/build/js/app.js:12525:57)
at constructClassInstance (http://dev.dummyreact/build/js/app.js:50422:18)
at updateClassComponent (http://dev.dummyreact/build/js/app.js:53748:5)
at beginWork (http://dev.dummyreact/build/js/app.js:54705:16)
at performUnitOfWork (http://dev.dummyreact/build/js/app.js:58373:12)
at workLoop (http://dev.dummyreact/build/js/app.js:58413:24)
at HTMLUnknownElement.callCallback (http://dev.dummyreact/build/js/app.js:39210:14)
at Object.invokeGuardedCallbackDev (http://dev.dummyreact/build/js/app.js:39260:16)
at invokeGuardedCallback (http://dev.dummyreact/build/js/app.js:39317:31)
The above error occurred in the <Connect(Form(BaseForm))> component:
in Connect(Form(BaseForm)) (created by ReduxForm)
in ReduxForm (created by Liform)
in Liform (created by App)
in div (created by App)
in App (created by ConnectFunction)
in ConnectFunction
in Provider
Эта ошибка произошла на стороне сервера и на стороне клиента:
код
В моем приложении единственная сущность называется Page
и имеет атрибут: name
.
Первое, что я делаю, это создаю Symfony / Type
PageType.php
<?php
namespace Osef\AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
class PageType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('name',TextType::class,[
'label' => 'Name',
'required' => true,
'attr' => [
'placeholder' => 'Some name'
],
'liform' => [
'description' => 'Description liform'
]]
);
}
}
Затем я даю форму представлению, используя $liform->transform()
метод
DefaultController.php
<?php
namespace Osef\AppBundle\Controller;
use Limenius\Liform\Liform;
use Osef\AppBundle\Entity\Page;
use Osef\AppBundle\Form\Type\PageType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller {
public function indexAction(Liform $liform) {
$page = new Page();
$form = $this->createForm(PageType::class, $page);
return $this->render("@OsefApp/index.html.twig", array(
"schema" => $liform->transform($form)
));
}
}
Внутри своего шаблона ветки я использую расширение limenius
для ветки, чтобы вызывать мои реагирующие компоненты, которые регистрируются по responseOnReacts на стороне JS.
Кроме того, я передаю параметры схемы (из PHP) в магазин.
index.html.twig
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
{{ encore_entry_link_tags('css/app') }}
{% endblock %}
</head>
<body>
<div id="react-container">{% spaceless %}{{ redux_store('TestStore', {
'schema': schema
}) }}{{ react_component('TestApp') }}{% endspaceless %}</div>
{% block javascripts %}
{{ encore_entry_script_tags('js/app') }}
{% endblock %}
</body>
</html>
Это хорошо для стороны PHP. Теперь давайте посмотрим на код React.
Прежде всего это зарегистрировать мои компоненты в ReactOnRail, чтобы сделать их доступными для представления (код выше)
registration.js
import ReactOnRails from "react-on-rails";
import TestApp from './test/TestApp';
import TestStore from './test/store/TestStore';
ReactOnRails.register({ TestApp }); // The application
ReactOnRails.registerStore({ TestStore }); // Redux store
Затем я разработал магазин, в котором есть только одна переменная: schema
.
TestStore.js
import { createStore, applyMiddleware, compose } from "redux";
import thunkMiddleware from "redux-thunk";
import reducers from "../reducer";
import { initialStates } from "../reducer";
export default function configureStore(props) {
const { schema } = props;
const { testState } = initialStates;
const initialState = {
testState: {
...testState,
schema
}
};
let composeEnhancers =
(typeof window !== "undefined" &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
compose;
const store = createStore(
reducers,
initialState,
composeEnhancers(applyMiddleware(thunkMiddleware))
);
return store;
}
В магазине используется комбинация моего TestReducer и редуктора формы из упаковки redux-form
Редуктор / index.js
import testReducer from "./testReducer";
import { initialState as testState } from "./testReducer";
import { combineReducers } from "redux";
import { reducer as formReducer } from "redux-form";
export default combineReducers({
testState: testReducer,
form: formReducer
});
export const initialStates = {
testState
};
TestReducer имеет только фиктивное действие, а инициализация schema
равна нулю.
testReducer.js
export const initialState = {
schema: null
};
export default function testReducer(state = initialState, action) {
switch (action.type) {
case "CHANGED":
return { ...state, schema: null };
default:
return state;
}
}
Наконец, вот мой код приложения
App.js
import React from "react";
import { connect } from "react-redux";
import Liform from "liform-react";
const App = (props) => {
const { schema } = props;
console.log("The props are correctly mapped at the store : "+JSON.stringify(schema));
const submit = () => console.log("submit")
// The application throw a error at this call of <Liform>
return (
<div>
<h1>Form :</h1>
<Liform
schema={schema}
onSubmit={submit}
/>
</div>
)
};
export default connect(state => ({
schema: state.testState.schema
}))(App);
В этом последнем файле мы видим, что я подключаю компонент App
к хранилищу, извлек схему из реквизита.
Console.log () здесь для проверки правильности внедрения схемы (что означает, что хранилище можно подключить отсюда).
Я сделал репозиторий с завершенным кодом, на ветке storeerror
.
https://github.com/ghauray/dummyreact/tree/storeerror
Примечание. Репо можно запустить с http-сервером и выполнить следующие действия:
php composer.phar install
npm install
// In separate consoles
npm run serverside-watch
npm run watch
Наконец
Итак, если вы уже допустили эту ошибку, или вы видите некоторые ошибки в моем коде, или если у вас есть какие-либо идеи, не стесняйтесь поделиться со мной!